diff --git a/README.md b/README.md index bffa93df1..67cd2e116 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ - Get Java 17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html - Get [MongoDB Community Server](https://www.mongodb.com/try/download/community) -- Get game version REL3.7 (3.7 client can be found here if you don't have it): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md +- Get game version REL4.0.x (4.0.x client can be found here if you don't have it): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/4.0.0.md - Download the [latest Cultivation version](https://github.com/Grasscutters/Cultivation/releases/latest). Use the `.msi` installer. - After opening Culivation (as admin), press the download button in the upper right corner. @@ -46,25 +46,49 @@ Grasscutter uses Gradle to handle dependencies & building. **Requirements:** -- [Java SE Development Kits - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or higher +- [Java Development Kit 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or higher - [Git](https://git-scm.com/downloads) +- [NodeJS](https://nodejs.org/en/download) (Optional, for building the handbook) -##### Windows +##### Clone ```shell git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git cd Grasscutter -.\gradlew.bat # Setting up environments -.\gradlew jar # Compile ``` -##### Linux (GNU) +##### Compile + +**Note**: Handbook generation may fail on some systems. To disable the handbook generation, append `-PskipHandbook=1` to the `gradlew jar` command. + +Windows: + +```shell +.\gradlew.bat # Setting up environments +.\gradlew jar +``` + +Linux (GNU): ```bash -git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git -cd Grasscutter chmod +x gradlew -./gradlew jar # Compile +./gradlew jar +``` + +##### Compiling the Handbook (Manually) + +With Gradle: + +```shell +./gradlew generateHandbook +``` + +With NPM: + +```shell +cd src/handbook +npm install +npm run build ``` You can find the output jar in the root of the project folder. diff --git a/docs/README_HE.md b/docs/README_HE.md index e98882d10..1fd3f9b66 100644 --- a/docs/README_HE.md +++ b/docs/README_HE.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **תשומת לב בבקשה:** אנחנו מקבלים עזרה בפיתוח התוכנה. לפני שאתם תורמים לפרויקט בבקשה תקראו את [תנאי השימוש](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_NL.md b/docs/README_NL.md index b620a149c..e746f4acc 100644 --- a/docs/README_NL.md +++ b/docs/README_NL.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Aantekening:** We verwelkomen altijd bijdragers aan het project. Lees onze [Gedragscode](https://github.com/Grasscutters/Grasscutter/blob/development/README_NL.md#bijdragen-aan-het-project) zorgvuldig door voordat u uw bijdrage toevoegt. diff --git a/docs/README_es-ES.md b/docs/README_es-ES.md index 661ee86ff..f436d2309 100644 --- a/docs/README_es-ES.md +++ b/docs/README_es-ES.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Atención:** Siempre damos la bienvenida a contribuidores del proyecto. Antes de añadir tu contribución, por favor lee cuidadosamente nuestro [Código de conducta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_fil-PH.md b/docs/README_fil-PH.md index 57e2198fa..77d3da4df 100644 --- a/docs/README_fil-PH.md +++ b/docs/README_fil-PH.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Atensyon:** Ang mga kontributor ay laging welcome sa proyektong ito. Bago mag-bigay ng kontribusyon, basahin muna ng mabuti ang [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_fr-FR.md b/docs/README_fr-FR.md index 2644f9011..93890f8ff 100644 --- a/docs/README_fr-FR.md +++ b/docs/README_fr-FR.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Attention:** De nouveaux contributeurs sont toujours les bienvenus. Avant d'ajouter votre contribution, veuillez lire le [code de conduite](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). @@ -71,4 +71,4 @@ Vous pouvez trouver le jar de sortie dans la racine du dossier du projet. ### Dépanage -Pour une liste des problèmes communs et leur solution et pour demander de l'aide, veuillez rejoindre [notre serveur Discord](https://discord.gg/T5vZU6UyeG) (en anglais) et dirigez vous vers le salon de support. \ No newline at end of file +Pour une liste des problèmes communs et leur solution et pour demander de l'aide, veuillez rejoindre [notre serveur Discord](https://discord.gg/T5vZU6UyeG) (en anglais) et dirigez vous vers le salon de support. diff --git a/docs/README_hn-IN.md b/docs/README_hn-IN.md new file mode 100644 index 000000000..0d33308b9 --- /dev/null +++ b/docs/README_hn-IN.md @@ -0,0 +1,78 @@ +![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light) +
Documentation GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
+ +
Discord - Grasscutter
+ +[EN](README.md) | [简中](docs/README_zh-CN.md) | [繁中](docs/README_zh-TW.md) | [FR](docs/README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) + +**ध्यान:** हम हमेशा परियोजना में योगदानकर्ताओं का स्वागत करते हैं।. अपना योगदान जोड़ने से पहले कृपया हमारा ध्यानपूर्वक पढ़ें [आचार संहिता](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). + +## वर्तमान सुविधाएँ + +* लॉग इन करना +* युद्ध +* मित्रों की सूची +* टेलीपोर्टेशन +* गाचा प्रणाली +* सह-ऑप * आंशिक रूप से * काम करता है +* कंसोल के माध्यम से राक्षसों को जन्म देना +* इन्वेंट्री सुविधाएँ (आइटम / वर्ण प्राप्त करना, आइटम / वर्णों को अपग्रेड करना, आदि) + +## त्वरित सेटअप गाइड + +**टिप्पणी**: समर्थन के लिए कृपया हमसे जुड़ें [Discord](https://discord.gg/T5vZU6UyeG). + +### त्वरित प्रारंभ (स्वचालित) + +- Get Java 17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html + +**ध्यान दें:** बस **सर्वर शुरू करने** के लिए, आपको बस **jre** की आवश्यकता है। +- Get [MongoDB Community Server](https://www.mongodb.com/try/download/community) + +* प्रॉक्सी: मिटमडंप (अनुशंसित), मिटमप्रॉक्सी, फिडलर क्लासिक, आदि। +- गेम संस्करण REL3.7 प्राप्त करें (यदि आपके पास 3.7 क्लाइंट नहीं है तो उसे यहां पाया जा सकता है):: https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md + +- डाउनलोड करें [latest Cultivation version](https://github.com/Grasscutters/Cultivation/releases/latest). उपयोग `.msi` इंस्टालरr. +- कलिवेशन (एडमिन के रूप में) खोलने के बाद, ऊपरी दाएं कोने में डाउनलोड बटन दबाएं। +- `डाउनलोड ऑल-इन-वन` पर क्लिक करें +- ऊपरी दाएं कोने में गियर पर क्लिक करें +- गेम इंस्टॉल पथ को उस स्थान पर सेट करें जहां आपका गेम स्थित है. +- कस्टम जावा पथ को इस पर सेट करें `C:\Program Files\Java\jdk-17\bin\java.exe` +- अन्य सभी सेटिंग्स को डिफ़ॉल्ट पर छोड़ दें + +- लॉन्च करने के लिए आगे छोटे बटन पर क्लिक करें. +- लॉन्च बटन पर क्लिक करें. +- आप जो भी उपयोगकर्ता नाम चाहते हैं उसके साथ लॉग इन करें। पासवर्ड कोई मायने नहीं रखता. + +### इमारत + +ग्रासकटर निर्भरता और निर्माण को संभालने के लिए ग्रैडल का उपयोग करता है। + +**आवश्यकताएं:** + +- [Java SE Development Kits - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or higher +- [Git](https://git-scm.com/downloads) + +##### विंडोज + +```shell +git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +.\gradlew.bat # Setting up environments +.\gradlew jar # Compile +``` + +##### लिनक्स (जीएनयू) + +```bash +git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +chmod +x gradlew +./gradlew jar # Compile +``` + +आप आउटपुट जार को प्रोजेक्ट फ़ोल्डर के रूट में पा सकते हैं।. + +### समस्या निवारण + +सामान्य मुद्दों और समाधानों की सूची और सहायता मांगने के लिए कृपया शामिल हों [our Discord server](https://discord.gg/T5vZU6UyeG) और सपोर्ट चैनल पर जाएं. \ No newline at end of file diff --git a/docs/README_id-ID.md b/docs/README_id-ID.md index ccac54424..a8de721a8 100644 --- a/docs/README_id-ID.md +++ b/docs/README_id-ID.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Perhatian:** Kami selalu menyambut kontributor untuk proyek ini. Sebelum menambahkan kontribusi Anda, harap baca [Kode Etik](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) kami. diff --git a/docs/README_it-IT.md b/docs/README_it-IT.md index 8eb4fa6f2..4b2f3e6b0 100644 --- a/docs/README_it-IT.md +++ b/docs/README_it-IT.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Attenzione:** Diamo sempre il benvenuto ai contributori del progetto. Prima di contribuire, leggi attentamente il nostro [Codice di condotta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_ja-JP.md b/docs/README_ja-JP.md index e117fab7f..99ae00d3f 100644 --- a/docs/README_ja-JP.md +++ b/docs/README_ja-JP.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) ***:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。貢献を追加する前に、我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください。 diff --git a/docs/README_ko-KR.md b/docs/README_ko-KR.md index 3f655b73b..af9cc20da 100644 --- a/docs/README_ko-KR.md +++ b/docs/README_ko-KR.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **주의 :** 우리는 항상 프로젝트에 기여하는 사람들을 환영합니다. 기여를 하기 전, [행동 지침](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)을 주의 깊게 읽어주세요. diff --git a/docs/README_pl-PL.md b/docs/README_pl-PL.md index 406ee33f6..c22819a54 100644 --- a/docs/README_pl-PL.md +++ b/docs/README_pl-PL.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Uwaga:** Zawsze jesteśmy otwarci na wasz wkład w projekt. Przed zaproponowaniem zmian przeczytaj [zasady postępowania (ENG)](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_ru-RU.md b/docs/README_ru-RU.md index cf1e95e0a..f3239e59c 100644 --- a/docs/README_ru-RU.md +++ b/docs/README_ru-RU.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Внимание:** Мы всегда рады новому вкладу в проект. Однако, перед тем, как сделать свой вклад, пожалуйста, прочтите наш [кодекс делового поведения](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_vi-VN.md b/docs/README_vi-VN.md index 7a6d22460..11e6d00ac 100644 --- a/docs/README_vi-VN.md +++ b/docs/README_vi-VN.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **Chú ý:** Chúng tôi luôn chào đón những người đóng góp cho dự án. Trước khi đóng góp, xin vui lòng đọc kỹ ["các quy tắc" (Code of Conduct)](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) của chúng tôi . diff --git a/docs/README_zh-CN.md b/docs/README_zh-CN.md index 7f631f8aa..76fd11dee 100644 --- a/docs/README_zh-CN.md +++ b/docs/README_zh-CN.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **注意:** 我们始终欢迎项目的贡献者。但在做贡献之前,请仔细阅读我们的[代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。 @@ -26,7 +26,7 @@ - 获取Java 17:https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html - 获取[MongoDB社区版](https://www.mongodb.com/try/download/community) -- 获取游戏3.7正式版 (如果你没有3.7的客户端,可以在这里找到):https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md) +- 获取游戏4.0正式版 (如果你没有4.0的客户端,可以在这里找到):https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/4.0.0.md) - 下载[最新的Cultivation版本](https://github.com/Grasscutters/Cultivation/releases/latest)(使用以“.msi”为后缀的安装包)。 - 以管理员身份打开Culivation,按右上角的下载按钮。 diff --git a/docs/README_zh-TW.md b/docs/README_zh-TW.md index 2d93bd783..a7816c116 100644 --- a/docs/README_zh-TW.md +++ b/docs/README_zh-TW.md @@ -3,7 +3,7 @@
Discord - Grasscutter
-[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) +[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **請注意:** 歡迎成為本專案的貢獻者。在提交 PR 之前, 請仔細閱讀[程式碼規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。 diff --git a/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java b/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java index e41b29c80..5e5a599f8 100644 --- a/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java +++ b/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java @@ -36,21 +36,21 @@ public final class HomePlantSubFieldDataOuterClass { int getEntityIdList(int index); /** - * .HomePlantFieldStatus CAKDDMKAIMD = 7; - * @return The enum numeric value on the wire for cAKDDMKAIMD. + * .HomePlantFieldStatus status = 7; + * @return The enum numeric value on the wire for status. */ - int getCAKDDMKAIMDValue(); + int getStatusValue(); /** - * .HomePlantFieldStatus CAKDDMKAIMD = 7; - * @return The cAKDDMKAIMD. + * .HomePlantFieldStatus status = 7; + * @return The status. */ - emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD(); + emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus(); /** - * uint32 JHFNDBIHLNB = 8; - * @return The jHFNDBIHLNB. + * uint32 seed_id = 8; + * @return The seedId. */ - int getJHFNDBIHLNB(); + int getSeedId(); /** * fixed32 end_time = 14; @@ -59,10 +59,10 @@ public final class HomePlantSubFieldDataOuterClass { int getEndTime(); /** - * uint32 KHFGOPCOAGM = 3; - * @return The kHFGOPCOAGM. + * uint32 gather_point_type = 3; + * @return The gatherPointType. */ - int getKHFGOPCOAGM(); + int getGatherPointType(); } /** *
@@ -82,7 +82,7 @@ public final class HomePlantSubFieldDataOuterClass {
     }
     private HomePlantSubFieldData() {
       entityIdList_ = emptyIntList();
-      cAKDDMKAIMD_ = 0;
+      status_ = 0;
     }
 
     @java.lang.Override
@@ -118,7 +118,7 @@ public final class HomePlantSubFieldDataOuterClass {
               break;
             case 24: {
 
-              kHFGOPCOAGM_ = input.readUInt32();
+              gatherPointType_ = input.readUInt32();
               break;
             }
             case 48: {
@@ -145,12 +145,12 @@ public final class HomePlantSubFieldDataOuterClass {
             case 56: {
               int rawValue = input.readEnum();
 
-              cAKDDMKAIMD_ = rawValue;
+              status_ = rawValue;
               break;
             }
             case 64: {
 
-              jHFNDBIHLNB_ = input.readUInt32();
+              seedId_ = input.readUInt32();
               break;
             }
             case 117: {
@@ -221,34 +221,34 @@ public final class HomePlantSubFieldDataOuterClass {
     }
     private int entityIdListMemoizedSerializedSize = -1;
 
-    public static final int CAKDDMKAIMD_FIELD_NUMBER = 7;
-    private int cAKDDMKAIMD_;
+    public static final int STATUS_FIELD_NUMBER = 7;
+    private int status_;
     /**
-     * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-     * @return The enum numeric value on the wire for cAKDDMKAIMD.
+     * .HomePlantFieldStatus status = 7;
+     * @return The enum numeric value on the wire for status.
      */
-    @java.lang.Override public int getCAKDDMKAIMDValue() {
-      return cAKDDMKAIMD_;
+    @java.lang.Override public int getStatusValue() {
+      return status_;
     }
     /**
-     * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-     * @return The cAKDDMKAIMD.
+     * .HomePlantFieldStatus status = 7;
+     * @return The status.
      */
-    @java.lang.Override public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD() {
+    @java.lang.Override public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus() {
       @SuppressWarnings("deprecation")
-      emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(cAKDDMKAIMD_);
+      emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(status_);
       return result == null ? emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.UNRECOGNIZED : result;
     }
 
-    public static final int JHFNDBIHLNB_FIELD_NUMBER = 8;
-    private int jHFNDBIHLNB_;
+    public static final int SEED_ID_FIELD_NUMBER = 8;
+    private int seedId_;
     /**
-     * uint32 JHFNDBIHLNB = 8;
-     * @return The jHFNDBIHLNB.
+     * uint32 seed_id = 8;
+     * @return The seedId.
      */
     @java.lang.Override
-    public int getJHFNDBIHLNB() {
-      return jHFNDBIHLNB_;
+    public int getSeedId() {
+      return seedId_;
     }
 
     public static final int END_TIME_FIELD_NUMBER = 14;
@@ -262,15 +262,15 @@ public final class HomePlantSubFieldDataOuterClass {
       return endTime_;
     }
 
-    public static final int KHFGOPCOAGM_FIELD_NUMBER = 3;
-    private int kHFGOPCOAGM_;
+    public static final int GATHER_POINT_TYPE_FIELD_NUMBER = 3;
+    private int gatherPointType_;
     /**
-     * uint32 KHFGOPCOAGM = 3;
-     * @return The kHFGOPCOAGM.
+     * uint32 gather_point_type = 3;
+     * @return The gatherPointType.
      */
     @java.lang.Override
-    public int getKHFGOPCOAGM() {
-      return kHFGOPCOAGM_;
+    public int getGatherPointType() {
+      return gatherPointType_;
     }
 
     private byte memoizedIsInitialized = -1;
@@ -288,8 +288,8 @@ public final class HomePlantSubFieldDataOuterClass {
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
       getSerializedSize();
-      if (kHFGOPCOAGM_ != 0) {
-        output.writeUInt32(3, kHFGOPCOAGM_);
+      if (gatherPointType_ != 0) {
+        output.writeUInt32(3, gatherPointType_);
       }
       if (getEntityIdListList().size() > 0) {
         output.writeUInt32NoTag(50);
@@ -298,11 +298,11 @@ public final class HomePlantSubFieldDataOuterClass {
       for (int i = 0; i < entityIdList_.size(); i++) {
         output.writeUInt32NoTag(entityIdList_.getInt(i));
       }
-      if (cAKDDMKAIMD_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) {
-        output.writeEnum(7, cAKDDMKAIMD_);
+      if (status_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) {
+        output.writeEnum(7, status_);
       }
-      if (jHFNDBIHLNB_ != 0) {
-        output.writeUInt32(8, jHFNDBIHLNB_);
+      if (seedId_ != 0) {
+        output.writeUInt32(8, seedId_);
       }
       if (endTime_ != 0) {
         output.writeFixed32(14, endTime_);
@@ -316,9 +316,9 @@ public final class HomePlantSubFieldDataOuterClass {
       if (size != -1) return size;
 
       size = 0;
-      if (kHFGOPCOAGM_ != 0) {
+      if (gatherPointType_ != 0) {
         size += com.google.protobuf.CodedOutputStream
-          .computeUInt32Size(3, kHFGOPCOAGM_);
+          .computeUInt32Size(3, gatherPointType_);
       }
       {
         int dataSize = 0;
@@ -334,13 +334,13 @@ public final class HomePlantSubFieldDataOuterClass {
         }
         entityIdListMemoizedSerializedSize = dataSize;
       }
-      if (cAKDDMKAIMD_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) {
+      if (status_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) {
         size += com.google.protobuf.CodedOutputStream
-          .computeEnumSize(7, cAKDDMKAIMD_);
+          .computeEnumSize(7, status_);
       }
-      if (jHFNDBIHLNB_ != 0) {
+      if (seedId_ != 0) {
         size += com.google.protobuf.CodedOutputStream
-          .computeUInt32Size(8, jHFNDBIHLNB_);
+          .computeUInt32Size(8, seedId_);
       }
       if (endTime_ != 0) {
         size += com.google.protobuf.CodedOutputStream
@@ -363,13 +363,13 @@ public final class HomePlantSubFieldDataOuterClass {
 
       if (!getEntityIdListList()
           .equals(other.getEntityIdListList())) return false;
-      if (cAKDDMKAIMD_ != other.cAKDDMKAIMD_) return false;
-      if (getJHFNDBIHLNB()
-          != other.getJHFNDBIHLNB()) return false;
+      if (status_ != other.status_) return false;
+      if (getSeedId()
+          != other.getSeedId()) return false;
       if (getEndTime()
           != other.getEndTime()) return false;
-      if (getKHFGOPCOAGM()
-          != other.getKHFGOPCOAGM()) return false;
+      if (getGatherPointType()
+          != other.getGatherPointType()) return false;
       if (!unknownFields.equals(other.unknownFields)) return false;
       return true;
     }
@@ -385,14 +385,14 @@ public final class HomePlantSubFieldDataOuterClass {
         hash = (37 * hash) + ENTITY_ID_LIST_FIELD_NUMBER;
         hash = (53 * hash) + getEntityIdListList().hashCode();
       }
-      hash = (37 * hash) + CAKDDMKAIMD_FIELD_NUMBER;
-      hash = (53 * hash) + cAKDDMKAIMD_;
-      hash = (37 * hash) + JHFNDBIHLNB_FIELD_NUMBER;
-      hash = (53 * hash) + getJHFNDBIHLNB();
+      hash = (37 * hash) + STATUS_FIELD_NUMBER;
+      hash = (53 * hash) + status_;
+      hash = (37 * hash) + SEED_ID_FIELD_NUMBER;
+      hash = (53 * hash) + getSeedId();
       hash = (37 * hash) + END_TIME_FIELD_NUMBER;
       hash = (53 * hash) + getEndTime();
-      hash = (37 * hash) + KHFGOPCOAGM_FIELD_NUMBER;
-      hash = (53 * hash) + getKHFGOPCOAGM();
+      hash = (37 * hash) + GATHER_POINT_TYPE_FIELD_NUMBER;
+      hash = (53 * hash) + getGatherPointType();
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -532,13 +532,13 @@ public final class HomePlantSubFieldDataOuterClass {
         super.clear();
         entityIdList_ = emptyIntList();
         bitField0_ = (bitField0_ & ~0x00000001);
-        cAKDDMKAIMD_ = 0;
+        status_ = 0;
 
-        jHFNDBIHLNB_ = 0;
+        seedId_ = 0;
 
         endTime_ = 0;
 
-        kHFGOPCOAGM_ = 0;
+        gatherPointType_ = 0;
 
         return this;
       }
@@ -572,10 +572,10 @@ public final class HomePlantSubFieldDataOuterClass {
           bitField0_ = (bitField0_ & ~0x00000001);
         }
         result.entityIdList_ = entityIdList_;
-        result.cAKDDMKAIMD_ = cAKDDMKAIMD_;
-        result.jHFNDBIHLNB_ = jHFNDBIHLNB_;
+        result.status_ = status_;
+        result.seedId_ = seedId_;
         result.endTime_ = endTime_;
-        result.kHFGOPCOAGM_ = kHFGOPCOAGM_;
+        result.gatherPointType_ = gatherPointType_;
         onBuilt();
         return result;
       }
@@ -634,17 +634,17 @@ public final class HomePlantSubFieldDataOuterClass {
           }
           onChanged();
         }
-        if (other.cAKDDMKAIMD_ != 0) {
-          setCAKDDMKAIMDValue(other.getCAKDDMKAIMDValue());
+        if (other.status_ != 0) {
+          setStatusValue(other.getStatusValue());
         }
-        if (other.getJHFNDBIHLNB() != 0) {
-          setJHFNDBIHLNB(other.getJHFNDBIHLNB());
+        if (other.getSeedId() != 0) {
+          setSeedId(other.getSeedId());
         }
         if (other.getEndTime() != 0) {
           setEndTime(other.getEndTime());
         }
-        if (other.getKHFGOPCOAGM() != 0) {
-          setKHFGOPCOAGM(other.getKHFGOPCOAGM());
+        if (other.getGatherPointType() != 0) {
+          setGatherPointType(other.getGatherPointType());
         }
         this.mergeUnknownFields(other.unknownFields);
         onChanged();
@@ -755,87 +755,87 @@ public final class HomePlantSubFieldDataOuterClass {
         return this;
       }
 
-      private int cAKDDMKAIMD_ = 0;
+      private int status_ = 0;
       /**
-       * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-       * @return The enum numeric value on the wire for cAKDDMKAIMD.
+       * .HomePlantFieldStatus status = 7;
+       * @return The enum numeric value on the wire for status.
        */
-      @java.lang.Override public int getCAKDDMKAIMDValue() {
-        return cAKDDMKAIMD_;
+      @java.lang.Override public int getStatusValue() {
+        return status_;
       }
       /**
-       * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-       * @param value The enum numeric value on the wire for cAKDDMKAIMD to set.
+       * .HomePlantFieldStatus status = 7;
+       * @param value The enum numeric value on the wire for status to set.
        * @return This builder for chaining.
        */
-      public Builder setCAKDDMKAIMDValue(int value) {
+      public Builder setStatusValue(int value) {
         
-        cAKDDMKAIMD_ = value;
+        status_ = value;
         onChanged();
         return this;
       }
       /**
-       * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-       * @return The cAKDDMKAIMD.
+       * .HomePlantFieldStatus status = 7;
+       * @return The status.
        */
       @java.lang.Override
-      public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD() {
+      public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus() {
         @SuppressWarnings("deprecation")
-        emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(cAKDDMKAIMD_);
+        emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(status_);
         return result == null ? emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.UNRECOGNIZED : result;
       }
       /**
-       * .HomePlantFieldStatus CAKDDMKAIMD = 7;
-       * @param value The cAKDDMKAIMD to set.
+       * .HomePlantFieldStatus status = 7;
+       * @param value The status to set.
        * @return This builder for chaining.
        */
-      public Builder setCAKDDMKAIMD(emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus value) {
+      public Builder setStatus(emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus value) {
         if (value == null) {
           throw new NullPointerException();
         }
         
-        cAKDDMKAIMD_ = value.getNumber();
+        status_ = value.getNumber();
         onChanged();
         return this;
       }
       /**
-       * .HomePlantFieldStatus CAKDDMKAIMD = 7;
+       * .HomePlantFieldStatus status = 7;
        * @return This builder for chaining.
        */
-      public Builder clearCAKDDMKAIMD() {
+      public Builder clearStatus() {
         
-        cAKDDMKAIMD_ = 0;
+        status_ = 0;
         onChanged();
         return this;
       }
 
-      private int jHFNDBIHLNB_ ;
+      private int seedId_ ;
       /**
-       * uint32 JHFNDBIHLNB = 8;
-       * @return The jHFNDBIHLNB.
+       * uint32 seed_id = 8;
+       * @return The seedId.
        */
       @java.lang.Override
-      public int getJHFNDBIHLNB() {
-        return jHFNDBIHLNB_;
+      public int getSeedId() {
+        return seedId_;
       }
       /**
-       * uint32 JHFNDBIHLNB = 8;
-       * @param value The jHFNDBIHLNB to set.
+       * uint32 seed_id = 8;
+       * @param value The seedId to set.
        * @return This builder for chaining.
        */
-      public Builder setJHFNDBIHLNB(int value) {
+      public Builder setSeedId(int value) {
         
-        jHFNDBIHLNB_ = value;
+        seedId_ = value;
         onChanged();
         return this;
       }
       /**
-       * uint32 JHFNDBIHLNB = 8;
+       * uint32 seed_id = 8;
        * @return This builder for chaining.
        */
-      public Builder clearJHFNDBIHLNB() {
+      public Builder clearSeedId() {
         
-        jHFNDBIHLNB_ = 0;
+        seedId_ = 0;
         onChanged();
         return this;
       }
@@ -871,33 +871,33 @@ public final class HomePlantSubFieldDataOuterClass {
         return this;
       }
 
-      private int kHFGOPCOAGM_ ;
+      private int gatherPointType_ ;
       /**
-       * uint32 KHFGOPCOAGM = 3;
-       * @return The kHFGOPCOAGM.
+       * uint32 gather_point_type = 3;
+       * @return The gatherPointType.
        */
       @java.lang.Override
-      public int getKHFGOPCOAGM() {
-        return kHFGOPCOAGM_;
+      public int getGatherPointType() {
+        return gatherPointType_;
       }
       /**
-       * uint32 KHFGOPCOAGM = 3;
-       * @param value The kHFGOPCOAGM to set.
+       * uint32 gather_point_type = 3;
+       * @param value The gatherPointType to set.
        * @return This builder for chaining.
        */
-      public Builder setKHFGOPCOAGM(int value) {
+      public Builder setGatherPointType(int value) {
         
-        kHFGOPCOAGM_ = value;
+        gatherPointType_ = value;
         onChanged();
         return this;
       }
       /**
-       * uint32 KHFGOPCOAGM = 3;
+       * uint32 gather_point_type = 3;
        * @return This builder for chaining.
        */
-      public Builder clearKHFGOPCOAGM() {
+      public Builder clearGatherPointType() {
         
-        kHFGOPCOAGM_ = 0;
+        gatherPointType_ = 0;
         onChanged();
         return this;
       }
@@ -969,12 +969,12 @@ public final class HomePlantSubFieldDataOuterClass {
   static {
     java.lang.String[] descriptorData = {
       "\n\033HomePlantSubFieldData.proto\032\032HomePlant" +
-      "FieldStatus.proto\"\227\001\n\025HomePlantSubFieldD" +
-      "ata\022\026\n\016entity_id_list\030\006 \003(\r\022*\n\013CAKDDMKAI" +
-      "MD\030\007 \001(\0162\025.HomePlantFieldStatus\022\023\n\013JHFND" +
-      "BIHLNB\030\010 \001(\r\022\020\n\010end_time\030\016 \001(\007\022\023\n\013KHFGOP" +
-      "COAGM\030\003 \001(\rB\033\n\031emu.grasscutter.net.proto" +
-      "b\006proto3"
+      "FieldStatus.proto\"\224\001\n\025HomePlantSubFieldD" +
+      "ata\022\026\n\016entity_id_list\030\006 \003(\r\022%\n\006status\030\007 " +
+      "\001(\0162\025.HomePlantFieldStatus\022\017\n\007seed_id\030\010 " +
+      "\001(\r\022\020\n\010end_time\030\016 \001(\007\022\031\n\021gather_point_ty" +
+      "pe\030\003 \001(\rB\033\n\031emu.grasscutter.net.protob\006p" +
+      "roto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
@@ -986,7 +986,7 @@ public final class HomePlantSubFieldDataOuterClass {
     internal_static_HomePlantSubFieldData_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_HomePlantSubFieldData_descriptor,
-        new java.lang.String[] { "EntityIdList", "CAKDDMKAIMD", "JHFNDBIHLNB", "EndTime", "KHFGOPCOAGM", });
+        new java.lang.String[] { "EntityIdList", "Status", "SeedId", "EndTime", "GatherPointType", });
     emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.getDescriptor();
   }
 
diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java
index 400cade1e..24b9cbb7e 100644
--- a/src/main/java/emu/grasscutter/data/GameData.java
+++ b/src/main/java/emu/grasscutter/data/GameData.java
@@ -286,6 +286,10 @@ public final class GameData {
     private static final Int2ObjectMap homeWorldBgmDataMap =
             new Int2ObjectOpenHashMap<>();
 
+    @Getter
+    private static final Int2ObjectMap homeWorldEventDataMap =
+            new Int2ObjectOpenHashMap<>();
+
     @Getter
     private static final Int2ObjectMap homeWorldLevelDataMap =
             new Int2ObjectOpenHashMap<>();
diff --git a/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
new file mode 100644
index 000000000..704ef73ce
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
@@ -0,0 +1,38 @@
+package emu.grasscutter.data.excels;
+
+import com.google.gson.annotations.SerializedName;
+import emu.grasscutter.data.GameResource;
+import emu.grasscutter.data.ResourceType;
+import emu.grasscutter.game.home.suite.event.SuiteEventType;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+@ResourceType(name = "HomeWorldEventExcelConfigData.json")
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Getter
+public class HomeWorldEventData extends GameResource {
+    @SerializedName(
+            value = "id",
+            alternate = {"BBEIIPEFDPE"})
+    int id;
+
+    @SerializedName(
+            value = "eventType",
+            alternate = {"JOCKIMECHDP"})
+    SuiteEventType eventType;
+
+    int avatarID;
+
+    @SerializedName(
+            value = "talkId",
+            alternate = {"IGNJAICDFPD"})
+    int talkId;
+
+    int rewardID;
+
+    @SerializedName(
+            value = "suiteId",
+            alternate = {"FEHOKMJPOED"})
+    int suiteId;
+}
diff --git a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
index 426849aa9..40e61c339 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
@@ -8,6 +8,7 @@ import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.proto.VisionTypeOuterClass;
 import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
 import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
+import java.util.concurrent.atomic.AtomicBoolean;
 import lombok.Getter;
 
 public class EntityHomeAnimal extends EntityMonster implements Rebornable {
@@ -15,7 +16,7 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable {
     private final Position rebornPos;
     @Getter private final int rebirth;
     @Getter private final int rebirthCD;
-    private boolean disappeared;
+    private final AtomicBoolean disappeared = new AtomicBoolean();
 
     public EntityHomeAnimal(Scene scene, HomeWorldAnimalData data, Position pos) {
         super(scene, GameData.getMonsterDataMap().get(data.getMonsterID()), pos, 1);
@@ -60,13 +61,13 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable {
                         new PacketSceneEntityDisappearNotify(
                                 this, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE));
         this.rebornCDTickCount = this.getRebornCD();
-        this.disappeared = true;
+        this.disappeared.set(true);
     }
 
     @Override
     public void reborn() {
-        if (this.disappeared) {
-            this.disappeared = false;
+        if (this.disappeared.get()) {
+            this.disappeared.set(false);
             this.getPosition().set(this.getRebornPos());
             this.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(this));
         }
@@ -74,6 +75,6 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable {
 
     @Override
     public boolean isInCD() {
-        return this.disappeared;
+        return this.disappeared.get();
     }
 }
diff --git a/src/main/java/emu/grasscutter/game/home/GameHome.java b/src/main/java/emu/grasscutter/game/home/GameHome.java
index 52f58e3f6..d4a2348c5 100644
--- a/src/main/java/emu/grasscutter/game/home/GameHome.java
+++ b/src/main/java/emu/grasscutter/game/home/GameHome.java
@@ -17,6 +17,7 @@ import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import lombok.AccessLevel;
 import lombok.Builder;
 import lombok.Data;
@@ -55,6 +56,7 @@ public class GameHome {
     Set unlockedHomeBgmList;
     int enterHomeOption;
     Map> finishedTalkIdMap;
+    Set finishedRewardEventIdSet;
 
     public static GameHome getByUid(Integer uid) {
         var home = DatabaseHelper.getHomeByUid(uid);
@@ -62,7 +64,9 @@ public class GameHome {
             home = GameHome.create(uid);
         }
 
+        home.reassignIfNull();
         home.fixMainHouseIfOld();
+        home.syncHomeAvatarCostume();
 
         return home;
     }
@@ -79,9 +83,19 @@ public class GameHome {
                 .mainHouseMap(new ConcurrentHashMap<>())
                 .unlockedHomeBgmList(new HashSet<>())
                 .finishedTalkIdMap(new HashMap<>())
+                .finishedRewardEventIdSet(new HashSet<>())
                 .build();
     }
 
+    // avoid NPE caused by database remover.
+    private void reassignIfNull() {
+        this.getSceneMap().values().stream()
+                .map(HomeSceneItem::getBlockItems)
+                .map(Map::values)
+                .flatMap(Collection::stream)
+                .forEach(HomeBlockItem::reassignIfNull);
+    }
+
     // Data fixer.
     private void fixMainHouseIfOld() {
         if (this.getMainHouseMap() == null) {
@@ -97,6 +111,18 @@ public class GameHome {
         this.save();
     }
 
+    private void syncHomeAvatarCostume() {
+        Stream.of(this.sceneMap, this.mainHouseMap)
+                .map(ConcurrentHashMap::values)
+                .flatMap(Collection::stream)
+                .map(HomeSceneItem::getBlockItems)
+                .map(Map::values)
+                .flatMap(Collection::stream)
+                .map(HomeBlockItem::getDeployNPCList)
+                .flatMap(Collection::stream)
+                .forEach(npc -> npc.setCostumeId(this.getPlayer().getCostumeFrom(npc.getAvatarId())));
+    }
+
     public void save() {
         DatabaseHelper.saveHome(this);
     }
@@ -113,12 +139,12 @@ public class GameHome {
                     if (defaultItem != null) {
                         Grasscutter.getLogger()
                                 .info("Set player {} home {} to initial setting", ownerUid, sceneId);
-                        return HomeSceneItem.parseFrom(defaultItem, sceneId);
                     } else {
                         // Realm res missing bricks account, use default realm data to allow main house
                         defaultItem = GameData.getHomeworldDefaultSaveData().get(2001);
-                        return HomeSceneItem.parseFrom(defaultItem, sceneId);
                     }
+
+                    return HomeSceneItem.parseFrom(defaultItem, sceneId);
                 });
     }
 
@@ -149,6 +175,8 @@ public class GameHome {
         this.getMainHouseMap().remove(outdoor); // delete main house in current scene.
         this.getMainHouseItem(outdoor); // put new main house with default arrangement.
         this.save();
+
+        this.getPlayer().getCurHomeWorld().getModuleManager().refreshMainHouse();
     }
 
     public void onOwnerLogin(Player player) {
@@ -160,6 +188,8 @@ public class GameHome {
         player.getSession().send(new PacketHomeMarkPointNotify(player));
         player.getSession().send(new PacketHomeAvatarTalkFinishInfoNotify(player));
         player.getSession().send(new PacketHomeAllUnlockedBgmIdListNotify(player));
+        player.getSession().send(new PacketHomeAvatarRewardEventNotify(player));
+        player.getSession().send(new PacketHomeAvatarAllFinishRewardNotify(player));
         checkAccumulatedResources(player);
         player.getSession().send(new PacketHomeResourceNotify(player));
     }
@@ -226,6 +256,20 @@ public class GameHome {
                 .toList();
     }
 
+    public boolean onClaimAvatarRewards(int eventId) {
+        if (this.finishedRewardEventIdSet == null) {
+            this.finishedRewardEventIdSet = new HashSet<>();
+        }
+
+        var success = this.finishedRewardEventIdSet.add(eventId);
+        this.save();
+        return success;
+    }
+
+    public boolean isRewardEventFinished(int eventId) {
+        return this.finishedRewardEventIdSet != null && this.finishedRewardEventIdSet.contains(eventId);
+    }
+
     public boolean addUnlockedHomeBgm(int homeBgmId) {
         if (!getUnlockedHomeBgmList().add(homeBgmId)) return false;
 
@@ -404,7 +448,7 @@ public class GameHome {
                 newCoin = storedCoin + owedCoin;
             }
             // Ensure max is not exceeded
-            storedCoin = (maxCoin >= newCoin) ? newCoin : maxCoin;
+            storedCoin = Math.min(maxCoin, newCoin);
         }
 
         // Update fetter exp
@@ -416,7 +460,7 @@ public class GameHome {
                 newFetter = storedFetterExp + owedFetter;
             }
             // Ensure max is not exceeded
-            storedFetterExp = (maxFetter >= newFetter) ? newFetter : maxFetter;
+            storedFetterExp = Math.min(maxFetter, newFetter);
         }
 
         save();
diff --git a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
index dbd080d46..69fe9f50a 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
@@ -2,6 +2,8 @@ package emu.grasscutter.game.home;
 
 import dev.morphia.annotations.*;
 import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
+import emu.grasscutter.game.home.suite.HomeSuiteItem;
+import emu.grasscutter.game.player.Player;
 import emu.grasscutter.net.proto.HomeBlockArrangementInfoOuterClass.HomeBlockArrangementInfo;
 import java.util.*;
 import java.util.stream.Stream;
@@ -19,6 +21,7 @@ public class HomeBlockItem {
     List persistentFurnitureList;
     List deployAnimalList;
     List deployNPCList;
+    List suiteList;
 
     public static HomeBlockItem parseFrom(HomeworldDefaultSaveData.HomeBlock homeBlock) {
         // create from default setting
@@ -37,10 +40,11 @@ public class HomeBlockItem {
                                         .toList())
                 .deployAnimalList(List.of())
                 .deployNPCList(List.of())
+                .suiteList(List.of())
                 .build();
     }
 
-    public void update(HomeBlockArrangementInfo homeBlockArrangementInfo) {
+    public void update(HomeBlockArrangementInfo homeBlockArrangementInfo, Player owner) {
         this.blockId = homeBlockArrangementInfo.getBlockId();
 
         this.deployFurnitureList =
@@ -60,7 +64,12 @@ public class HomeBlockItem {
 
         this.deployNPCList =
                 homeBlockArrangementInfo.getDeployNpcListList().stream()
-                        .map(HomeNPCItem::parseFrom)
+                        .map(homeNpcData -> HomeNPCItem.parseFrom(homeNpcData, owner))
+                        .toList();
+
+        this.suiteList =
+                homeBlockArrangementInfo.getFurnitureSuiteListList().stream()
+                        .map(HomeSuiteItem::parseFrom)
                         .toList();
     }
 
@@ -81,15 +90,20 @@ public class HomeBlockItem {
         this.persistentFurnitureList.forEach(f -> proto.addPersistentFurnitureList(f.toProto()));
         this.deployAnimalList.forEach(f -> proto.addDeployAnimalList(f.toProto()));
         this.deployNPCList.forEach(f -> proto.addDeployNpcList(f.toProto()));
+        this.suiteList.forEach(f -> proto.addFurnitureSuiteList(f.toProto()));
 
         return proto.build();
     }
 
-    // TODO add more types (farm field and suite)
+    // TODO implement farm field.
     public List getMarkPointProtoFactories() {
         this.reassignIfNull();
 
-        return Stream.of(this.deployFurnitureList, this.persistentFurnitureList, this.deployNPCList)
+        return Stream.of(
+                        this.deployFurnitureList,
+                        this.persistentFurnitureList,
+                        this.deployNPCList,
+                        this.suiteList)
                 .flatMap(Collection::stream)
                 .toList();
     }
@@ -107,5 +121,8 @@ public class HomeBlockItem {
         if (this.deployNPCList == null) {
             this.deployNPCList = List.of();
         }
+        if (this.suiteList == null) {
+            this.suiteList = List.of();
+        }
     }
 }
diff --git a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
new file mode 100644
index 000000000..26460c09b
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
@@ -0,0 +1,235 @@
+package emu.grasscutter.game.home;
+
+import com.github.davidmoten.guavamini.Lists;
+import emu.grasscutter.game.home.suite.event.HomeAvatarRewardEvent;
+import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent;
+import emu.grasscutter.game.home.suite.event.SuiteEventType;
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.proto.HomeAvatarRewardEventNotifyOuterClass;
+import emu.grasscutter.net.proto.HomeAvatarSummonAllEventNotifyOuterClass;
+import emu.grasscutter.net.proto.RetcodeOuterClass;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify;
+import emu.grasscutter.utils.Either;
+import java.util.*;
+import java.util.stream.Stream;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+@Getter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class HomeModuleManager {
+    final Player homeOwner;
+    final HomeWorld homeWorld;
+    final GameHome home;
+    final int moduleId;
+    final HomeScene outdoor;
+    HomeScene indoor;
+    final List rewardEvents;
+    final List summonEvents;
+
+    public HomeModuleManager(HomeWorld homeWorld) {
+        this.homeOwner = homeWorld.getHost();
+        this.homeWorld = homeWorld;
+        this.home = homeWorld.getHome();
+        this.moduleId = this.homeOwner.getCurrentRealmId();
+        this.outdoor = homeWorld.getSceneById(homeWorld.getActiveOutdoorSceneId());
+        this.refreshMainHouse();
+        this.rewardEvents = Lists.newArrayList();
+        this.summonEvents = Collections.synchronizedList(Lists.newArrayList());
+    }
+
+    public void tick() {
+        if (this.moduleId == 0) {
+            return;
+        }
+
+        this.outdoor.onTick();
+        this.indoor.onTick();
+        this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver);
+    }
+
+    public void refreshMainHouse() {
+        if (this.moduleId == 0) {
+            return;
+        }
+
+        this.indoor = this.homeWorld.getSceneById(this.homeWorld.getActiveIndoorSceneId());
+    }
+
+    public void onUpdateArrangement() {
+        this.fireAllAvatarRewardEvent();
+        this.cancelSummonEventIfAvatarLeave();
+    }
+
+    private void fireAllAvatarRewardEvent() {
+        this.rewardEvents.clear();
+        var allBlockItems =
+                Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem())
+                        .map(HomeSceneItem::getBlockItems)
+                        .map(Map::values)
+                        .flatMap(Collection::stream)
+                        .toList();
+
+        var suites =
+                allBlockItems.stream()
+                        .map(HomeBlockItem::getSuiteList)
+                        .filter(Objects::nonNull)
+                        .flatMap(Collection::stream)
+                        .distinct()
+                        .toList();
+
+        allBlockItems.stream()
+                .map(HomeBlockItem::getDeployNPCList)
+                .flatMap(Collection::stream)
+                .forEach(
+                        avatar -> {
+                            suites.forEach(
+                                    suite -> {
+                                        var data =
+                                                SuiteEventType.HOME_AVATAR_REWARD_EVENT.getEventDataFrom(
+                                                        avatar.getAvatarId(), suite.getSuiteId());
+                                        if (data == null || this.home.isRewardEventFinished(data.getId())) {
+                                            return;
+                                        }
+
+                                        this.rewardEvents.add(
+                                                new HomeAvatarRewardEvent(
+                                                        homeOwner,
+                                                        data.getId(),
+                                                        data.getRewardID(),
+                                                        data.getAvatarID(),
+                                                        data.getSuiteId(),
+                                                        suite.getGuid()));
+                                    });
+                        });
+
+        if (this.summonEvents != null) {
+            var suiteIdList = this.rewardEvents.stream().map(HomeAvatarRewardEvent::getSuiteId).toList();
+            this.summonEvents.removeIf(event -> suiteIdList.contains(event.getSuiteId()));
+        }
+    }
+
+    private void cancelSummonEventIfAvatarLeave() {
+        var avatars =
+                Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem())
+                        .map(HomeSceneItem::getBlockItems)
+                        .map(Map::values)
+                        .flatMap(Collection::stream)
+                        .map(HomeBlockItem::getDeployNPCList)
+                        .flatMap(Collection::stream)
+                        .map(HomeNPCItem::getAvatarId)
+                        .toList();
+
+        this.summonEvents.removeIf(event -> !avatars.contains(event.getAvatarId()));
+    }
+
+    public Either, Integer> claimAvatarRewards(int eventId) {
+        if (this.rewardEvents.isEmpty()) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE);
+        }
+
+        var event = this.rewardEvents.remove(0);
+        if (event.getEventId() != eventId) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE);
+        }
+
+        if (!this.homeOwner.getHome().onClaimAvatarRewards(eventId)) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE);
+        }
+
+        return Either.left(event.giveRewards());
+    }
+
+    public Either fireAvatarSummonEvent(
+            Player owner, int avatarId, int guid, int suiteId) {
+        var targetSuite =
+                ((HomeScene) owner.getScene())
+                        .getSceneItem().getBlockItems().values().stream()
+                                .map(HomeBlockItem::getSuiteList)
+                                .flatMap(Collection::stream)
+                                .filter(suite -> suite.getGuid() == guid)
+                                .findFirst()
+                                .orElse(null);
+
+        if (this.isInRewardEvent(avatarId)) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_DUPLICATE_AVATAR_VALUE);
+        }
+
+        if (this.rewardEvents.stream().anyMatch(event -> event.getGuid() == guid)) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_HOME_FURNITURE_GUID_ERROR_VALUE);
+        }
+
+        this.summonEvents.removeIf(event -> event.getGuid() == guid || event.getAvatarId() == avatarId);
+
+        if (targetSuite == null) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE);
+        }
+
+        var eventData = SuiteEventType.HOME_AVATAR_SUMMON_EVENT.getEventDataFrom(avatarId, suiteId);
+        if (eventData == null) {
+            return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE);
+        }
+
+        var event =
+                new HomeAvatarSummonEvent(
+                        owner, eventData.getId(), eventData.getRewardID(), avatarId, suiteId, guid);
+        this.summonEvents.add(event);
+        owner.sendPacket(new PacketHomeAvatarSummonAllEventNotify(owner));
+        return Either.left(event);
+    }
+
+    public void onFinishSummonEvent(int eventId) {
+        this.summonEvents.removeIf(event -> event.getEventId() == eventId);
+    }
+
+    public HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify toRewardEventProto() {
+        var notify = HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify.newBuilder();
+        if (!this.rewardEvents.isEmpty()) {
+            notify.setRewardEvent(this.rewardEvents.get(0).toProto()).setIsEventTrigger(true);
+
+            notify.addAllPendingList(
+                    this.rewardEvents.subList(1, this.rewardEvents.size()).stream()
+                            .map(HomeAvatarRewardEvent::toProto)
+                            .toList());
+        }
+
+        return notify.build();
+    }
+
+    public HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify
+            toSummonEventProto() {
+        return HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify.newBuilder()
+                .addAllSummonEventList(
+                        this.summonEvents.stream().map(HomeAvatarSummonEvent::toProto).toList())
+                .build();
+    }
+
+    public boolean isInRewardEvent(int avatarId) {
+        return this.rewardEvents.stream().anyMatch(e -> e.getAvatarId() == avatarId);
+    }
+
+    public HomeSceneItem getOutdoorSceneItem() {
+        return this.outdoor.getSceneItem();
+    }
+
+    public HomeSceneItem getIndoorSceneItem() {
+        return this.indoor.getSceneItem();
+    }
+
+    public void onSetModule() {
+        if (this.moduleId == 0) {
+            return;
+        }
+
+        this.outdoor.addEntities(this.getOutdoorSceneItem().getAnimals(this.outdoor));
+        this.indoor.addEntities(this.getIndoorSceneItem().getAnimals(this.indoor));
+        this.fireAllAvatarRewardEvent();
+    }
+
+    public void onRemovedModule() {
+        this.outdoor.getEntities().clear();
+        this.indoor.getEntities().clear();
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java
index fbd2ad377..7999fcd39 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java
@@ -2,6 +2,7 @@ package emu.grasscutter.game.home;
 
 import dev.morphia.annotations.Entity;
 import emu.grasscutter.data.GameData;
+import emu.grasscutter.game.player.Player;
 import emu.grasscutter.game.world.Position;
 import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass;
 import emu.grasscutter.net.proto.HomeMarkPointNPCDataOuterClass;
@@ -23,11 +24,12 @@ public class HomeNPCItem implements HomeMarkPointProtoFactory {
     Position spawnRot;
     int costumeId;
 
-    public static HomeNPCItem parseFrom(HomeNpcDataOuterClass.HomeNpcData homeNpcData) {
+    public static HomeNPCItem parseFrom(HomeNpcDataOuterClass.HomeNpcData homeNpcData, Player owner) {
         return HomeNPCItem.of()
                 .avatarId(homeNpcData.getAvatarId())
                 .spawnPos(new Position(homeNpcData.getSpawnPos()))
                 .spawnRot(new Position(homeNpcData.getSpawnRot()))
+                .costumeId(owner.getCostumeFrom(homeNpcData.getAvatarId()))
                 .build();
     }
 
diff --git a/src/main/java/emu/grasscutter/game/home/HomeScene.java b/src/main/java/emu/grasscutter/game/home/HomeScene.java
index b07f798b0..c03247c1d 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeScene.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeScene.java
@@ -1,9 +1,12 @@
 package emu.grasscutter.game.home;
 
 import emu.grasscutter.data.excels.scene.SceneData;
+import emu.grasscutter.game.entity.EntityHomeAnimal;
 import emu.grasscutter.game.entity.GameEntity;
+import emu.grasscutter.game.entity.Rebornable;
 import emu.grasscutter.game.player.Player;
 import emu.grasscutter.game.world.Scene;
+import emu.grasscutter.net.proto.VisionTypeOuterClass;
 import emu.grasscutter.server.packet.send.PacketSceneTimeNotify;
 
 public class HomeScene extends Scene {
@@ -40,10 +43,31 @@ public class HomeScene extends Scene {
                 .forEach(gameEntity -> gameEntity.onTick(this.getSceneTimeSeconds()));
 
         this.finishLoading();
-        this.checkPlayerRespawn();
         if (this.tickCount++ % 10 == 0) this.broadcastPacket(new PacketSceneTimeNotify(this));
     }
 
+    public void onEnterEditModeFinish() {
+        this.removeEntities(
+                this.getEntities().values().stream()
+                        .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal)
+                        .toList(),
+                VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
+    }
+
+    public void onLeaveEditMode() {
+        this.addEntities(this.getSceneItem().getAnimals(this));
+    }
+
+    @Override
+    public void killEntity(GameEntity target, int attackerId) {
+        if (target instanceof Rebornable rebornable) {
+            rebornable.onAiKillSelf(); // Teapot animals will not die. They will revive!
+            return;
+        }
+
+        super.killEntity(target, attackerId);
+    }
+
     @Override
     public void checkNpcGroup() {}
 
diff --git a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
index 7449f029f..93031537c 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
@@ -6,16 +6,18 @@ import emu.grasscutter.Grasscutter;
 import emu.grasscutter.data.GameData;
 import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
 import emu.grasscutter.game.entity.EntityHomeAnimal;
+import emu.grasscutter.game.player.Player;
 import emu.grasscutter.game.world.Position;
 import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.proto.HomeSceneArrangementInfoOuterClass.HomeSceneArrangementInfo;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
-import lombok.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
 import lombok.experimental.FieldDefaults;
 
 @Entity
@@ -49,14 +51,14 @@ public class HomeSceneItem {
                 .build();
     }
 
-    public void update(HomeSceneArrangementInfo arrangementInfo) {
+    public void update(HomeSceneArrangementInfo arrangementInfo, Player owner) {
         for (var blockItem : arrangementInfo.getBlockArrangementInfoListList()) {
             var block = this.blockItems.get(blockItem.getBlockId());
             if (block == null) {
                 Grasscutter.getLogger().warn("Could not found the Home Block {}", blockItem.getBlockId());
                 continue;
             }
-            block.update(blockItem);
+            block.update(blockItem, owner);
             this.blockItems.put(blockItem.getBlockId(), block);
         }
 
@@ -84,17 +86,13 @@ public class HomeSceneItem {
     }
 
     @Nullable public Position getTeleportPointPos(int guid) {
-        var pos = new AtomicReference();
-
-        this.getBlockItems().values().stream()
+        return this.getBlockItems().values().stream()
                 .map(HomeBlockItem::getDeployFurnitureList)
                 .flatMap(Collection::stream)
                 .filter(homeFurnitureItem -> homeFurnitureItem.getGuid() == guid)
                 .map(HomeFurnitureItem::getSpawnPos)
                 .findFirst()
-                .ifPresent(pos::set);
-
-        return pos.get();
+                .orElse(null);
     }
 
     public List getAnimals(Scene scene) {
diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorld.java b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
index f2ea9d3b6..525da1be4 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
@@ -8,33 +8,59 @@ import emu.grasscutter.game.world.World;
 import emu.grasscutter.net.packet.BasePacket;
 import emu.grasscutter.net.proto.ChatInfoOuterClass;
 import emu.grasscutter.server.game.GameServer;
-import emu.grasscutter.server.packet.send.*;
+import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
 import java.util.List;
+import java.util.function.Consumer;
 import lombok.Getter;
 
 public class HomeWorld extends World {
     @Getter private final GameHome home;
+    @Getter private HomeModuleManager moduleManager;
 
     public HomeWorld(GameServer server, Player owner) {
         super(server, owner);
 
         this.home = owner.isOnline() ? owner.getHome() : GameHome.getByUid(owner.getUid());
         server.registerHomeWorld(this);
+        this.refreshModuleManager();
     }
 
     @Override
-    public void registerScene(Scene scene) {
-        this.addAnimalsToScene((HomeScene) scene);
-        super.registerScene(scene);
+    public boolean onTick() {
+        if (this.moduleManager == null) {
+            return false;
+        }
+        this.moduleManager.tick();
+
+        if (this.getTickCount() % 10 == 0) {
+            this.getPlayers().forEach(p -> p.sendPacket(new PacketPlayerGameTimeNotify(p)));
+        }
+
+        if (this.isInHome(this.getHost()) && this.getTickCount() % 60 == 0) {
+            this.getHost().updatePlayerGameTime(this.getCurrentWorldTime());
+        }
+
+        this.tickCount++;
+        return false;
     }
 
-    @Override
-    public void deregisterScene(Scene scene) {
-        super.deregisterScene(scene);
+    public void refreshModuleManager() {
+        if (this.moduleManager != null) {
+            this.moduleManager.onRemovedModule();
+        }
+
+        this.moduleManager = new HomeModuleManager(this);
+        this.moduleManager.onSetModule();
     }
 
-    private void addAnimalsToScene(HomeScene scene) {
-        scene.getSceneItem().getAnimals(scene).forEach(scene::addEntity);
+    public int getActiveOutdoorSceneId() {
+        return this.getHost().getCurrentRealmId() + 2000;
+    }
+
+    public int getActiveIndoorSceneId() {
+        return this.getSceneById(this.getActiveOutdoorSceneId()).getSceneItem().getRoomSceneId();
     }
 
     @Override
@@ -188,6 +214,12 @@ public class HomeWorld extends World {
         return this.getPlayers().contains(player);
     }
 
+    public void ifHost(Player hostOrGuest, Consumer ifHost) {
+        if (this.getHost().equals(hostOrGuest)) {
+            ifHost.accept(hostOrGuest);
+        }
+    }
+
     public void sendPacketToHostIfOnline(BasePacket basePacket) {
         if (this.getHost().isOnline()) {
             this.getHost().sendPacket(basePacket);
diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
index aecacb21c..0835975b0 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
@@ -5,10 +5,7 @@ import emu.grasscutter.game.props.EnterReason;
 import emu.grasscutter.game.world.Position;
 import emu.grasscutter.game.world.World;
 import emu.grasscutter.game.world.data.TeleportProperties;
-import emu.grasscutter.net.proto.EnterTypeOuterClass;
-import emu.grasscutter.net.proto.OtherPlayerEnterHomeNotifyOuterClass;
-import emu.grasscutter.net.proto.PlayerApplyEnterHomeResultNotifyOuterClass;
-import emu.grasscutter.net.proto.RetcodeOuterClass;
+import emu.grasscutter.net.proto.*;
 import emu.grasscutter.server.event.player.PlayerEnterHomeEvent;
 import emu.grasscutter.server.event.player.PlayerLeaveHomeEvent;
 import emu.grasscutter.server.event.player.PlayerTeleportEvent;
@@ -215,6 +212,10 @@ public class HomeWorldMPSystem extends BaseGameSystem {
         player.setCurHomeWorld(myHome);
         myHome.getHome().onOwnerLogin(player);
 
+        player.sendPacket(
+                new PacketPlayerQuitFromHomeNotify(
+                        PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason
+                                .BACK_TO_MY_WORLD));
         player.sendPacket(
                 new PacketPlayerEnterSceneNotify(
                         player,
@@ -263,6 +264,9 @@ public class HomeWorldMPSystem extends BaseGameSystem {
         victim.setCurHomeWorld(myHome);
         myHome.getHome().onOwnerLogin(victim);
 
+        victim.sendPacket(
+                new PacketPlayerQuitFromHomeNotify(
+                        PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason.KICK_BY_HOST));
         victim.sendPacket(
                 new PacketPlayerEnterSceneNotify(
                         victim,
diff --git a/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java b/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java
new file mode 100644
index 000000000..88841e9fb
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java
@@ -0,0 +1,82 @@
+package emu.grasscutter.game.home.suite;
+
+import dev.morphia.annotations.Entity;
+import emu.grasscutter.game.home.HomeMarkPointProtoFactory;
+import emu.grasscutter.game.home.SpecialFurnitureType;
+import emu.grasscutter.game.world.Position;
+import emu.grasscutter.net.proto.HomeFurnitureSuiteDataOuterClass;
+import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass;
+import emu.grasscutter.net.proto.HomeMarkPointSuiteDataOuterClass;
+import java.util.List;
+import java.util.Objects;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+import org.jetbrains.annotations.Nullable;
+
+@Entity
+@Builder(builderMethodName = "of")
+@Getter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class HomeSuiteItem implements HomeMarkPointProtoFactory {
+    public static final int SUITE_FURNITURE_ID = 377101;
+    int guid;
+    int suiteId;
+    Position pos;
+    List includedFurnitureIndexList;
+    boolean isAllowSummon;
+
+    public static HomeSuiteItem parseFrom(
+            HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData data) {
+        return HomeSuiteItem.of()
+                .guid(data.getGuid())
+                .suiteId(data.getSuiteId())
+                .pos(new Position(data.getSpawnPos()))
+                .includedFurnitureIndexList(data.getIncludedFurnitureIndexListList())
+                .isAllowSummon(data.getIsAllowSummon())
+                .build();
+    }
+
+    public HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData toProto() {
+        return HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData.newBuilder()
+                .setSuiteId(this.suiteId)
+                .setGuid(this.guid)
+                .setIsAllowSummon(this.isAllowSummon)
+                .addAllIncludedFurnitureIndexList(this.includedFurnitureIndexList)
+                .setSpawnPos(this.pos.toProto())
+                .build();
+    }
+
+    @Nullable @Override
+    public HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto() {
+        return HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData.newBuilder()
+                .setFurnitureId(SUITE_FURNITURE_ID)
+                .setPos(this.pos.toProto())
+                .setFurnitureType(this.getType().getValue())
+                .setGuid(this.guid)
+                .setSuiteData(
+                        HomeMarkPointSuiteDataOuterClass.HomeMarkPointSuiteData.newBuilder()
+                                .setSuiteId(this.suiteId)
+                                .build())
+                .build();
+    }
+
+    @Override
+    public SpecialFurnitureType getType() {
+        return SpecialFurnitureType.FurnitureSuite;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        HomeSuiteItem that = (HomeSuiteItem) o;
+        return suiteId == that.suiteId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(suiteId);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java
new file mode 100644
index 000000000..06d2fc2a9
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java
@@ -0,0 +1,54 @@
+package emu.grasscutter.game.home.suite.event;
+
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.utils.Utils;
+import java.util.List;
+import java.util.Objects;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+@Getter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public abstract class HomeAvatarEvent {
+    final Player homeOwner;
+    final int eventId;
+    final int rewardId;
+    final int avatarId;
+    final int suiteId;
+    final int guid;
+    final int randomPos;
+
+    public HomeAvatarEvent(
+            Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) {
+        this.homeOwner = homeOwner;
+        this.eventId = eventId;
+        this.rewardId = rewardId;
+        this.avatarId = avatarId;
+        this.suiteId = suiteId;
+        this.guid = guid;
+        this.randomPos = this.generateRandomPos();
+    }
+
+    public int generateRandomPos() {
+        return Utils.randomRange(1, 97);
+    }
+
+    public List giveRewards() {
+        return List.of();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        HomeAvatarEvent that = (HomeAvatarEvent) o;
+        return eventId == that.eventId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(eventId);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java
new file mode 100644
index 000000000..de897faf0
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java
@@ -0,0 +1,37 @@
+package emu.grasscutter.game.home.suite.event;
+
+import emu.grasscutter.data.GameData;
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.game.props.ActionReason;
+import emu.grasscutter.net.proto.HomeAvatarRewardEventInfoOuterClass;
+import java.util.List;
+
+public class HomeAvatarRewardEvent extends HomeAvatarEvent {
+    public HomeAvatarRewardEvent(
+            Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) {
+        super(homeOwner, eventId, rewardId, avatarId, suiteId, guid);
+    }
+
+    public HomeAvatarRewardEventInfoOuterClass.HomeAvatarRewardEventInfo toProto() {
+        return HomeAvatarRewardEventInfoOuterClass.HomeAvatarRewardEventInfo.newBuilder()
+                .setAvatarId(this.getAvatarId())
+                .setEventId(this.getEventId())
+                .setGuid(this.getGuid())
+                .setSuiteId(this.getSuiteId())
+                .setRandomPosition(this.getRandomPos())
+                .build();
+    }
+
+    @Override
+    public List giveRewards() {
+        var data = GameData.getRewardDataMap().get(this.getRewardId());
+        if (data == null) {
+            return List.of();
+        }
+
+        var rewards = data.getRewardItemList().stream().map(GameItem::new).toList();
+        this.getHomeOwner().getInventory().addItems(rewards, ActionReason.HomeAvatarEventReward);
+        return rewards;
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
new file mode 100644
index 000000000..f6b9c0b5e
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
@@ -0,0 +1,37 @@
+package emu.grasscutter.game.home.suite.event;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.proto.HomeAvatarSummonEventInfoOuterClass;
+import emu.grasscutter.utils.Utils;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+@Getter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class HomeAvatarSummonEvent extends HomeAvatarEvent {
+    public static final int TIME_LIMIT_SECS = 240;
+    final int eventOverTime;
+
+    public HomeAvatarSummonEvent(
+            Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) {
+        super(homeOwner, eventId, rewardId, avatarId, suiteId, guid);
+
+        this.eventOverTime = Utils.getCurrentSeconds() + TIME_LIMIT_SECS;
+    }
+
+    public HomeAvatarSummonEventInfoOuterClass.HomeAvatarSummonEventInfo toProto() {
+        return HomeAvatarSummonEventInfoOuterClass.HomeAvatarSummonEventInfo.newBuilder()
+                .setAvatarId(this.getAvatarId())
+                .setEventId(this.getEventId())
+                .setGuid(this.getGuid())
+                .setSuitId(this.getSuiteId())
+                .setRandomPosition(this.getRandomPos())
+                .setEventOverTime(this.eventOverTime)
+                .build();
+    }
+
+    public boolean isTimeOver() {
+        return Utils.getCurrentSeconds() > this.eventOverTime;
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
new file mode 100644
index 000000000..6056744e8
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
@@ -0,0 +1,21 @@
+package emu.grasscutter.game.home.suite.event;
+
+import emu.grasscutter.data.GameData;
+import emu.grasscutter.data.excels.HomeWorldEventData;
+import javax.annotation.Nullable;
+
+public enum SuiteEventType {
+    HOME_AVATAR_REWARD_EVENT,
+    HOME_AVATAR_SUMMON_EVENT;
+
+    @Nullable public HomeWorldEventData getEventDataFrom(int avatarId, int suiteId) {
+        return GameData.getHomeWorldEventDataMap().values().stream()
+                .filter(
+                        data ->
+                                data.getEventType() == this
+                                        && data.getAvatarID() == avatarId
+                                        && data.getSuiteId() == suiteId)
+                .findFirst()
+                .orElse(null);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java
index d4b242d31..5adc13b80 100644
--- a/src/main/java/emu/grasscutter/game/player/Player.java
+++ b/src/main/java/emu/grasscutter/game/player/Player.java
@@ -950,6 +950,13 @@ public class Player implements PlayerHook, FieldFetch {
         this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
     }
 
+    public int getCostumeFrom(int avatarId) {
+        var avatars = this.getAvatars();
+        avatars.loadFromDatabase();
+        var avatar = avatars.getAvatarById(avatarId);
+        return avatar == null ? 0 : avatar.getCostume();
+    }
+
     public void addPersonalLine(int personalLineId) {
         this.getPersonalLineList().add(personalLineId);
         session.getPlayer().getQuestManager().queueEvent(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK, personalLineId);
diff --git a/src/main/java/emu/grasscutter/game/props/ActionReason.java b/src/main/java/emu/grasscutter/game/props/ActionReason.java
index 76a2aed58..57867ed35 100644
--- a/src/main/java/emu/grasscutter/game/props/ActionReason.java
+++ b/src/main/java/emu/grasscutter/game/props/ActionReason.java
@@ -177,7 +177,8 @@ public enum ActionReason {
     ChannellerSlabLoopDungeonFirstPassReward(1090),
     ChannellerSlabLoopDungeonScoreReward(1091),
     HomeLimitedShopBuy(1092),
-    HomeCoinCollect(1093);
+    HomeCoinCollect(1093),
+    HomeAvatarEventReward(1100);
 
     private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>();
     private static final Map stringMap = new HashMap<>();
diff --git a/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
index 06acd48e3..b258b8310 100644
--- a/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
+++ b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
@@ -169,23 +169,6 @@ public class GameMainQuest {
             this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED;
         }
 
-        /*
-         * We also need to check for unfinished childQuests in this MainQuest
-         * force them to complete and send a packet about this to the user,
-         * because at some points there are special "invisible" child quests that control
-         * some situations.
-         *
-         * For example, subQuest 35312 is responsible for the event of leaving the territory
-         * of the island with a statue and automatically returns the character back,
-         * quest 35311 completes the main quest line 353 and starts 35501 from
-         * new MainQuest 355 but if 35312 is not completed after the completion
-         * of the main quest 353 - the character will not be able to leave place
-         * (return again and again)
-         */
-        // this.getChildQuests().values().stream()
-        //         .filter(p -> p.state != QuestState.QUEST_STATE_FINISHED)
-        //         .forEach(GameQuest::finish);
-
         this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this));
         this.getOwner().getSession().send(new PacketCodexDataUpdateNotify(this));
 
@@ -383,46 +366,6 @@ public class GameMainQuest {
         }
     }
 
-    public void tryAcceptSubQuests(QuestCond condType, String paramStr, int... params) {
-        try {
-            List subQuestsWithCond =
-                    getChildQuests().values().stream()
-                            .filter(
-                                    p ->
-                                            p.getState() == QuestState.QUEST_STATE_UNSTARTED
-                                                    || p.getState() == QuestState.UNFINISHED)
-                            .filter(
-                                    p ->
-                                            p.getQuestData().getAcceptCond().stream()
-                                                    .anyMatch(
-                                                            q ->
-                                                                    condType == QuestCond.QUEST_COND_NONE || q.getType() == condType))
-                            .toList();
-            var questSystem = owner.getServer().getQuestSystem();
-
-            for (GameQuest subQuestWithCond : subQuestsWithCond) {
-                var acceptCond = subQuestWithCond.getQuestData().getAcceptCond();
-                int[] accept = new int[acceptCond.size()];
-
-                for (int i = 0; i < subQuestWithCond.getQuestData().getAcceptCond().size(); i++) {
-                    var condition = acceptCond.get(i);
-                    boolean result =
-                            questSystem.triggerCondition(
-                                    getOwner(), subQuestWithCond.getQuestData(), condition, paramStr, params);
-                    accept[i] = result ? 1 : 0;
-                }
-
-                boolean shouldAccept =
-                        LogicType.calculate(subQuestWithCond.getQuestData().getAcceptCondComb(), accept);
-
-                if (shouldAccept) subQuestWithCond.start();
-            }
-            this.save();
-        } catch (Exception e) {
-            Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
-        }
-    }
-
     public void tryFailSubQuests(QuestContent condType, String paramStr, int... params) {
         try {
             List subQuestsWithCond =
@@ -437,7 +380,7 @@ public class GameMainQuest {
             for (GameQuest subQuestWithCond : subQuestsWithCond) {
                 val failCond = subQuestWithCond.getQuestData().getFailCond();
 
-                for (int i = 0; i < subQuestWithCond.getQuestData().getFailCond().size(); i++) {
+                for (int i = 0; i < failCond.size(); i++) {
                     val condition = failCond.get(i);
                     if (condition.getType() == condType) {
                         boolean result =
@@ -445,7 +388,7 @@ public class GameMainQuest {
                                         .getServer()
                                         .getQuestSystem()
                                         .triggerContent(subQuestWithCond, condition, paramStr, params);
-                        subQuestWithCond.getFailProgressList()[i] = result ? 1 : 0;
+                        subQuestWithCond.setFailProgress(i, result ? 1 : 0);
                         if (result) {
                             getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
                         }
diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java
index c62b3c0ef..7888e4701 100644
--- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java
+++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java
@@ -158,6 +158,13 @@ public class GameQuest {
     public boolean clearProgress(boolean notifyDelete) {
         // TODO improve
         var oldState = state;
+        if (questData.getAcceptCond() != null && questData.getAcceptCond().size() != 0) {
+            this.getMainQuest()
+                    .getQuestManager()
+                    .getAcceptProgressLists()
+                    .put(this.getSubQuestId(), new int[questData.getAcceptCond().size()]);
+        }
+
         if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
             for (var condition : questData.getFinishCond()) {
                 if (condition.getType() == QuestContent.QUEST_CONTENT_LUA_NOTIFY) {
diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java
index 6611da1c7..a14350327 100644
--- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java
+++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java
@@ -26,6 +26,7 @@ public final class QuestManager extends BasePlayerManager {
     @Getter private final Player player;
 
     @Getter private final Int2ObjectMap mainQuests;
+    @Getter private Int2ObjectMap acceptProgressLists;
     @Getter private final List loggedQuests;
 
     private long lastHourCheck = 0;
@@ -52,6 +53,7 @@ public final class QuestManager extends BasePlayerManager {
         this.player = player;
         this.mainQuests = new Int2ObjectOpenHashMap<>();
         this.loggedQuests = new ArrayList<>();
+        this.acceptProgressLists = new Int2ObjectOpenHashMap<>();
 
         if (DEBUG) {
             this.loggedQuests.addAll(
@@ -484,8 +486,6 @@ public final class QuestManager extends BasePlayerManager {
         eventExecutor.submit(() -> triggerEvent(condType, paramStr, params));
     }
 
-    // QUEST_EXEC are handled directly by each subQuest
-
     public void triggerEvent(QuestCond condType, String paramStr, int... params) {
         Grasscutter.getLogger().trace("Trigger Event {}, {}, {}", condType, paramStr, params);
         var potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr);
@@ -502,15 +502,19 @@ public final class QuestManager extends BasePlayerManager {
                         return;
                     }
                     val acceptCond = questData.getAcceptCond();
-                    int[] accept = new int[acceptCond.size()];
+                    acceptProgressLists.putIfAbsent(questData.getId(), new int[acceptCond.size()]);
                     for (int i = 0; i < acceptCond.size(); i++) {
                         val condition = acceptCond.get(i);
-                        boolean result =
-                                questSystem.triggerCondition(owner, questData, condition, paramStr, params);
-                        accept[i] = result ? 1 : 0;
+                        if (condition.getType() == condType) {
+                            boolean result =
+                                    questSystem.triggerCondition(owner, questData, condition, paramStr, params);
+                            acceptProgressLists.get(questData.getId())[i] = result ? 1 : 0;
+                        }
                     }
 
-                    boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
+                    boolean shouldAccept =
+                            LogicType.calculate(
+                                    questData.getAcceptCondComb(), acceptProgressLists.get(questData.getId()));
                     if (this.loggedQuests.contains(questData.getId())) {
                         Grasscutter.getLogger()
                                 .debug(
@@ -522,7 +526,7 @@ public final class QuestManager extends BasePlayerManager {
                                         Arrays.stream(params)
                                                 .mapToObj(String::valueOf)
                                                 .collect(Collectors.joining(", ")));
-                        for (var i = 0; i < accept.length; i++) {
+                        for (var i = 0; i < acceptCond.size(); i++) {
                             var condition = acceptCond.get(i);
                             Grasscutter.getLogger()
                                     .debug(
@@ -532,14 +536,13 @@ public final class QuestManager extends BasePlayerManager {
                                                     .filter(value -> value > 0)
                                                     .mapToObj(String::valueOf)
                                                     .collect(Collectors.joining(", ")),
-                                            accept[i] == 1 ? "success" : "failure");
+                                            acceptProgressLists.get(questData.getId())[i] == 1 ? "success" : "failure");
                         }
                     }
 
                     if (shouldAccept) {
                         GameQuest quest = owner.getQuestManager().addQuest(questData);
-                        Grasscutter.getLogger()
-                                .debug("Added quest {} result {}", questData.getSubId(), quest != null);
+                        Grasscutter.getLogger().debug("Added quest {}", questData.getSubId());
                     }
                 });
     }
diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java
new file mode 100644
index 000000000..fe77519cb
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java
@@ -0,0 +1,21 @@
+package emu.grasscutter.game.quest.conditions;
+
+import emu.grasscutter.data.excels.quest.QuestData;
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.game.quest.QuestValueCond;
+import emu.grasscutter.game.quest.enums.QuestCond;
+
+@QuestValueCond(QuestCond.QUEST_COND_ITEM_GIVING_FINISHED)
+public class ConditionItemGivingFinished extends BaseCondition {
+
+    @Override
+    public boolean execute(
+            Player owner,
+            QuestData questData,
+            QuestData.QuestAcceptCondition condition,
+            String paramStr,
+            int... params) {
+        return condition.getParam()[0] == params[0]
+                && (condition.getParam()[1] == 0 || condition.getParam()[1] == params[1]);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java
similarity index 66%
rename from src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java
rename to src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java
index cd9ed53cd..01e3171b7 100644
--- a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java
+++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java
@@ -5,10 +5,11 @@ import emu.grasscutter.game.quest.*;
 import emu.grasscutter.game.quest.enums.QuestContent;
 
 @QuestValueContent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING)
-public final class ContentFinishGivingItem extends BaseContent {
+public final class ContentFinishItemGiving extends BaseContent {
     @Override
     public boolean execute(
             GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
-        return condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1];
+        return condition.getParam()[0] == params[0]
+                && (condition.getParam()[1] == 0 || condition.getParam()[1] == params[1]);
     }
 }
diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java
index 190b313e2..7b43c442d 100644
--- a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java
+++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java
@@ -25,7 +25,7 @@ public enum QuestCond implements QuestTrigger {
     QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER(17),
     QUEST_COND_SCENE_AREA_UNLOCKED(18), // missing, only NPC groups/talks
     QUEST_COND_ITEM_GIVING_ACTIVED(19), // missing
-    QUEST_COND_ITEM_GIVING_FINISHED(20), // missing
+    QUEST_COND_ITEM_GIVING_FINISHED(20),
     QUEST_COND_IS_DAYTIME(21), // only NPC groups
     QUEST_COND_CURRENT_AVATAR(22), // missing
     QUEST_COND_CURRENT_AREA(23), // missing
diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java
index eb0bd86ce..f9c629031 100644
--- a/src/main/java/emu/grasscutter/game/world/Scene.java
+++ b/src/main/java/emu/grasscutter/game/world/Scene.java
@@ -535,7 +535,17 @@ public class Scene {
                                 "Can not solve monster drop: drop_id = {}, drop_tag = {}. Falling back to legacy drop system.",
                                 monster.getMetaMonster().drop_id,
                                 monster.getMetaMonster().drop_tag);
-                getWorld().getServer().getDropSystemLegacy().callDrop(monster);
+                world.getServer().getDropSystemLegacy().callDrop(monster);
+            }
+        }
+
+        if (target instanceof EntityGadget gadget) {
+            if (gadget.getMetaGadget() != null) {
+                world
+                        .getServer()
+                        .getDropSystem()
+                        .handleChestDrop(
+                                gadget.getMetaGadget().drop_id, gadget.getMetaGadget().drop_count, gadget);
             }
         }
 
diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java
index 3b9e01ed0..085e60fe7 100644
--- a/src/main/java/emu/grasscutter/game/world/World.java
+++ b/src/main/java/emu/grasscutter/game/world/World.java
@@ -39,7 +39,7 @@ public class World implements Iterable {
     @Getter private boolean timeLocked;
 
     private long lastUpdateTime;
-    @Getter private int tickCount = 0;
+    @Getter protected int tickCount = 0;
     @Getter private boolean isPaused = false;
     @Getter private long currentWorldTime;
 
diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java
index 984c994ba..990ac11b4 100644
--- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java
+++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java
@@ -1105,6 +1105,12 @@ public class ScriptLib {
         return 0;
     }
 
+    public int MoveAvatarByPointArrayWithTemplate(int uid, int pointarray_id, int[] routelist, int var4, LuaTable var5){
+        logger.warn("[LUA] Call unimplemented MoveAvatarByPointArrayWithTemplate with {} {} {} {} {}", uid, pointarray_id, routelist, var4, printTable(var5));
+        //TODO implement var5 contains int speed
+        return 0;
+    }
+
     public int MovePlayerToPos(LuaTable var1){
         logger.warn("[LUA] Call unchecked MovePlayerToPos with {}", printTable(var1));
         //TODO implement var1 contains int[] uid_list, Position pos, int radius, Position rot
@@ -1262,6 +1268,7 @@ public class ScriptLib {
     }
 
     public int SetWeatherAreaState(int var1, int var2) {
+        logger.debug("[LUA] Call SetWeatherAreaState with {} {}", var1, var2);
         this.getSceneScriptManager().getScene().getPlayers()
             .forEach(p -> p.setWeather(var1, ClimateType.getTypeByValue(var2)));
         return 0;
@@ -1405,6 +1412,11 @@ public class ScriptLib {
         return 0;
     }
 
+    public int SetPlayerInteractOption(String var1){
+        logger.warn("[LUA] Call unimplemented SetPlayerInteractOption {}", var1);
+        return 0;
+    }
+
     public int UnlockForce(int force){
         logger.debug("[LUA] Call UnlockForce {}", force);
         getSceneScriptManager().getScene().unlockForce(force);
@@ -1552,7 +1564,7 @@ public class ScriptLib {
     }
 
     public int GetRegionConfigId(LuaTable var1){
-        logger.warn("[LUA] Call untested GetRegionConfigId with {}", printTable(var1));
+        logger.debug("[LUA] Call GetRegionConfigId with {}", printTable(var1));
         var EntityId = var1.get("region_eid").toint();
         var entity = getSceneScriptManager().getScene().getScriptManager().getRegionById(EntityId);
         if (entity == null){
@@ -1657,9 +1669,11 @@ public class ScriptLib {
     }
 
     public int SetGadgetEnableInteract(int groupId, int configId, boolean enable) {
-        EntityGadget gadget = getCurrentEntityGadget();
-        if(gadget.getGroupId() != groupId || gadget.getConfigId() != configId) return -1;
-
+        logger.debug("[LUA] Call SetGadgetEnableInteract with {} {} {}", groupId, configId, enable);
+        var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, groupId);
+        if (!(entity instanceof EntityGadget gadget)) {
+            return -1;
+        }
         gadget.setInteractEnabled(enable);
 
         return 0;
diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java
index 253a67e7d..8c8821ca7 100644
--- a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java
+++ b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java
@@ -7,6 +7,7 @@ import lombok.*;
 public class SceneGadget extends SceneObject {
     public int gadget_id;
     public int chest_drop_id;
+    public int drop_id;
     public int drop_count;
     public String drop_tag;
     boolean showcutscene;
diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
index 77706c7ec..ea4b2ece3 100644
--- a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
+++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
@@ -30,16 +30,47 @@ public class SceneRegion {
 
     public boolean contains(Position position) {
         switch (shape) {
-            case ScriptRegionShape.CUBIC:
+            case ScriptRegionShape.SPHERE -> {
+                val x = pos.getX() - position.getX();
+                val y = pos.getY() - position.getY();
+                val z = pos.getZ() - position.getZ();
+                // x^2 + y^2 + z^2 = radius^2
+                return x * x + y * y + z * z <= radius * radius;
+            }
+            case ScriptRegionShape.CUBIC -> {
                 return (Math.abs(pos.getX() - position.getX()) <= size.getX() / 2f)
                         && (Math.abs(pos.getY() - position.getY()) <= size.getY() / 2f)
                         && (Math.abs(pos.getZ() - position.getZ()) <= size.getZ() / 2f);
-            case ScriptRegionShape.SPHERE:
-                var x = Math.pow(pos.getX() - position.getX(), 2);
-                var y = Math.pow(pos.getY() - position.getY(), 2);
-                var z = Math.pow(pos.getZ() - position.getZ(), 2);
-                // ^ means XOR in java!
-                return x + y + z <= (radius * radius);
+            }
+            case ScriptRegionShape.POLYGON -> {
+                // algorithm is "ray casting": https://www.youtube.com/watch?v=RSXM9bgqxJM
+                if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false;
+                var count = 0;
+                for (var i = 0; i < point_array.size(); ++i) {
+                    val j = (i + 1) % point_array.size();
+
+                    val yp = position.getZ();
+                    val y1 = point_array.get(i).getY();
+                    val y2 = point_array.get(j).getY();
+
+                    val xp = position.getX();
+                    val x1 = point_array.get(i).getX();
+                    val x2 = point_array.get(j).getX();
+
+                    if ((yp < y1) != (yp < y2) && xp < x1 + ((yp - y1) / (y2 - y1)) * (x2 - x1)) {
+                        ++count;
+                    }
+                }
+
+                return count % 2 == 1;
+            }
+            case ScriptRegionShape.CYLINDER -> {
+                if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false;
+                val x = pos.getX() - position.getX();
+                val z = pos.getZ() - position.getZ();
+                // x^2 + z^2 = radius^2
+                return x * x + z * z <= radius * radius;
+            }
         }
         return false;
     }
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
new file mode 100644
index 000000000..6b616795b
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
@@ -0,0 +1,29 @@
+package emu.grasscutter.server.packet.recv;
+
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarRewardEventGetReqOuterClass;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarAllFinishRewardNotify;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarRewardEventGetRsp;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarRewardEventNotify;
+
+@Opcodes(PacketOpcodes.HomeAvatarRewardEventGetReq)
+public class HandlerHomeAvatarRewardEventGetReq extends PacketHandler {
+    @Override
+    public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+        var req = HomeAvatarRewardEventGetReqOuterClass.HomeAvatarRewardEventGetReq.parseFrom(payload);
+
+        var player = session.getPlayer();
+        var rewardsOrError =
+                player.getCurHomeWorld().getModuleManager().claimAvatarRewards(req.getEventId());
+        session.send(new PacketHomeAvatarRewardEventNotify(player));
+        session.send(new PacketHomeAvatarAllFinishRewardNotify(player));
+
+        session.send(
+                rewardsOrError.map(
+                        gameItems -> new PacketHomeAvatarRewardEventGetRsp(req.getEventId(), gameItems),
+                        integer -> new PacketHomeAvatarRewardEventGetRsp(req.getEventId(), integer)));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
new file mode 100644
index 000000000..ada4476b3
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
@@ -0,0 +1,22 @@
+package emu.grasscutter.server.packet.recv;
+
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarSummonEventReqOuterClass;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonEventRsp;
+
+@Opcodes(PacketOpcodes.HomeAvatarSummonEventReq)
+public class HandlerHomeAvatarSummonEventReq extends PacketHandler {
+    @Override
+    public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+        var req = HomeAvatarSummonEventReqOuterClass.HomeAvatarSummonEventReq.parseFrom(payload);
+        var moduleManager = session.getPlayer().getCurHomeWorld().getModuleManager();
+        var eventOrError =
+                moduleManager.fireAvatarSummonEvent(
+                        session.getPlayer(), req.getAvatarId(), req.getGuid(), req.getSuitId());
+        session.send(
+                eventOrError.map(PacketHomeAvatarSummonEventRsp::new, PacketHomeAvatarSummonEventRsp::new));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java
new file mode 100644
index 000000000..4027c041a
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java
@@ -0,0 +1,21 @@
+package emu.grasscutter.server.packet.recv;
+
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarSummonFinishReqOuterClass;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify;
+import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonFinishRsp;
+
+@Opcodes(PacketOpcodes.HomeAvatarSummonFinishReq)
+public class HandlerHomeAvatarSummonFinishReq extends PacketHandler {
+    @Override
+    public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+        var req = HomeAvatarSummonFinishReqOuterClass.HomeAvatarSummonFinishReq.parseFrom(payload);
+        var player = session.getPlayer();
+        player.getCurHomeWorld().getModuleManager().onFinishSummonEvent(req.getEventId());
+        session.send(new PacketHomeAvatarSummonAllEventNotify(session.getPlayer()));
+        session.send(new PacketHomeAvatarSummonFinishRsp(req.getEventId()));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java
index 4d6ae5dfa..3befd5b9e 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java
@@ -1,5 +1,6 @@
 package emu.grasscutter.server.packet.recv;
 
+import emu.grasscutter.game.home.HomeScene;
 import emu.grasscutter.net.packet.Opcodes;
 import emu.grasscutter.net.packet.PacketHandler;
 import emu.grasscutter.net.packet.PacketOpcodes;
@@ -31,14 +32,8 @@ public class HandlerHomeChangeEditModeReq extends PacketHandler {
         session.send(new PacketHomeComfortInfoNotify(session.getPlayer()));
 
         if (!req.getIsEnterEditMode()) {
-            var scene = session.getPlayer().getScene();
-            scene.addEntities(
-                    session
-                            .getPlayer()
-                            .getCurHomeWorld()
-                            .getHome()
-                            .getHomeSceneItem(scene.getId())
-                            .getAnimals(scene));
+            var scene = (HomeScene) session.getPlayer().getScene();
+            scene.onLeaveEditMode();
         }
 
         session.send(new PacketHomeChangeEditModeRsp(req.getIsEnterEditMode()));
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
index cff46aab2..e05831d40 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
@@ -1,7 +1,5 @@
 package emu.grasscutter.server.packet.recv;
 
-import emu.grasscutter.game.world.Position;
-import emu.grasscutter.game.world.Scene;
 import emu.grasscutter.net.packet.Opcodes;
 import emu.grasscutter.net.packet.PacketHandler;
 import emu.grasscutter.net.packet.PacketOpcodes;
@@ -21,7 +19,8 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
         HomeChangeModuleReqOuterClass.HomeChangeModuleReq req =
                 HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
 
-        if (!session.getPlayer().getCurHomeWorld().getGuests().isEmpty()) {
+        var homeWorld = session.getPlayer().getCurHomeWorld();
+        if (!homeWorld.getGuests().isEmpty()) {
             session.send(new PacketHomeChangeModuleRsp());
             return;
         }
@@ -33,13 +32,10 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
         session.send(new PacketHomeComfortInfoNotify(session.getPlayer()));
 
         int realmId = 2000 + req.getTargetModuleId();
+        var scene = homeWorld.getSceneById(realmId);
+        var pos = scene.getScriptManager().getConfig().born_pos;
 
-        Scene scene = session.getPlayer().getWorld().getSceneById(realmId);
-        Position pos = scene.getScriptManager().getConfig().born_pos;
-
-        session
-                .getPlayer()
-                .getWorld()
-                .transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos);
+        homeWorld.transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos);
+        homeWorld.refreshModuleManager();
     }
 }
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java
index 7890726e0..9f4f8f5ae 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java
@@ -1,10 +1,9 @@
 package emu.grasscutter.server.packet.recv;
 
-import emu.grasscutter.game.entity.EntityHomeAnimal;
+import emu.grasscutter.game.home.HomeScene;
 import emu.grasscutter.net.packet.Opcodes;
 import emu.grasscutter.net.packet.PacketHandler;
 import emu.grasscutter.net.packet.PacketOpcodes;
-import emu.grasscutter.net.proto.VisionTypeOuterClass;
 import emu.grasscutter.server.game.GameSession;
 import emu.grasscutter.server.packet.send.PacketHomeEnterEditModeFinishRsp;
 
@@ -17,12 +16,8 @@ public class HandlerHomeEnterEditModeFinishReq extends PacketHandler {
          * This packet is about the edit mode
          */
 
-        var scene = session.getPlayer().getScene();
-        scene.removeEntities(
-                scene.getEntities().values().stream()
-                        .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal)
-                        .toList(),
-                VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
+        var scene = (HomeScene) session.getPlayer().getScene();
+        scene.onEnterEditModeFinish();
 
         session.send(new PacketHomeEnterEditModeFinishRsp());
     }
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java
index 2932a663b..4f7e972c7 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java
@@ -1,6 +1,8 @@
 package emu.grasscutter.server.packet.recv;
 
-import emu.grasscutter.net.packet.*;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
 import emu.grasscutter.net.proto.OtherPlayerEnterHomeNotifyOuterClass;
 import emu.grasscutter.server.game.GameSession;
 import emu.grasscutter.server.packet.send.*;
@@ -26,6 +28,12 @@ public class HandlerHomeSceneInitFinishReq extends PacketHandler {
             }
         }
 
+        curHomeWorld.ifHost(
+                session.getPlayer(),
+                player -> {
+                    player.sendPacket(new PacketHomeAvatarRewardEventNotify(player));
+                    player.sendPacket(new PacketHomeAvatarSummonAllEventNotify(player));
+                });
         session.send(new PacketHomeMarkPointNotify(session.getPlayer()));
 
         session.send(new PacketHomeSceneInitFinishRsp());
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java
index 2b93ac718..e605dc091 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java
@@ -5,10 +5,7 @@ import emu.grasscutter.net.packet.PacketHandler;
 import emu.grasscutter.net.packet.PacketOpcodes;
 import emu.grasscutter.net.proto.HomeUpdateArrangementInfoReqOuterClass;
 import emu.grasscutter.server.game.GameSession;
-import emu.grasscutter.server.packet.send.PacketHomeAvatarTalkFinishInfoNotify;
-import emu.grasscutter.server.packet.send.PacketHomeBasicInfoNotify;
-import emu.grasscutter.server.packet.send.PacketHomeMarkPointNotify;
-import emu.grasscutter.server.packet.send.PacketHomeUpdateArrangementInfoRsp;
+import emu.grasscutter.server.packet.send.*;
 
 @Opcodes(PacketOpcodes.HomeUpdateArrangementInfoReq)
 public class HandlerHomeUpdateArrangementInfoReq extends PacketHandler {
@@ -22,14 +19,17 @@ public class HandlerHomeUpdateArrangementInfoReq extends PacketHandler {
                 session.getPlayer().getHome().getHomeSceneItem(session.getPlayer().getSceneId());
 
         var roomSceneId = homeScene.getRoomSceneId();
-        homeScene.update(req.getSceneArrangementInfo());
+        homeScene.update(req.getSceneArrangementInfo(), session.getPlayer());
         if (roomSceneId != homeScene.getRoomSceneId()) {
             session.getPlayer().getHome().onMainHouseChanged();
         }
 
+        session.getPlayer().getCurHomeWorld().getModuleManager().onUpdateArrangement();
+        session.send(new PacketHomeAvatarRewardEventNotify(session.getPlayer()));
         session.send(
                 new PacketHomeBasicInfoNotify(session.getPlayer(), session.getPlayer().isInEditMode()));
         session.send(new PacketHomeAvatarTalkFinishInfoNotify(session.getPlayer()));
+        session.send(new PacketHomeAvatarSummonAllEventNotify(session.getPlayer()));
         session.send(new PacketHomeMarkPointNotify(session.getPlayer()));
 
         session.getPlayer().getHome().save();
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java
index 15b73fcf0..23db1bfb8 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java
@@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv;
 
 import emu.grasscutter.Grasscutter;
 import emu.grasscutter.data.GameData;
+import emu.grasscutter.game.quest.enums.QuestCond;
 import emu.grasscutter.game.quest.enums.QuestContent;
 import emu.grasscutter.net.packet.*;
 import emu.grasscutter.net.proto.ItemGivingReqOuterClass.ItemGivingReq;
@@ -50,8 +51,9 @@ public final class HandlerItemGivingReq extends PacketHandler {
                         player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS));
                         // Remove the action from the active givings.
                         questManager.removeGivingItemAction(giveId);
-                        // Queue the content action.
+                        // Queue the content and condition actions.
                         questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0);
+                        questManager.queueEvent(QuestCond.QUEST_COND_ITEM_GIVING_FINISHED, giveId, 0);
                     }
                     case GIVING_METHOD_VAGUE_GROUP -> {
                         var matchedGroups = new ArrayList();
@@ -96,8 +98,11 @@ public final class HandlerItemGivingReq extends PacketHandler {
                             player.sendPacket(new PacketItemGivingRsp(matchedGroups.get(0), Mode.GROUP_SUCCESS));
                             // Mark the giving action as completed.
                             questManager.markCompleted(giveId);
-                            // Queue the content action.
-                            questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0);
+                            // Queue the content and condition actions.
+                            questManager.queueEvent(
+                                    QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, matchedGroups.get(0));
+                            questManager.queueEvent(
+                                    QuestCond.QUEST_COND_ITEM_GIVING_FINISHED, giveId, matchedGroups.get(0));
                         }
                     }
                 }
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
new file mode 100644
index 000000000..df07429f9
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
@@ -0,0 +1,19 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarAllFinishRewardNotifyOuterClass;
+
+public class PacketHomeAvatarAllFinishRewardNotify extends BasePacket {
+    public PacketHomeAvatarAllFinishRewardNotify(Player player) {
+        super(PacketOpcodes.HomeAvatarAllFinishRewardNotify);
+
+        var list = player.getHome().getFinishedRewardEventIdSet();
+        if (list != null) {
+            this.setData(
+                    HomeAvatarAllFinishRewardNotifyOuterClass.HomeAvatarAllFinishRewardNotify.newBuilder()
+                            .addAllEventIdList(player.getHome().getFinishedRewardEventIdSet()));
+        }
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
new file mode 100644
index 000000000..8bff115ec
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
@@ -0,0 +1,27 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarRewardEventGetRspOuterClass;
+import java.util.List;
+
+public class PacketHomeAvatarRewardEventGetRsp extends BasePacket {
+    public PacketHomeAvatarRewardEventGetRsp(int eventId, List rewards) {
+        super(PacketOpcodes.HomeAvatarRewardEventGetRsp);
+
+        this.setData(
+                HomeAvatarRewardEventGetRspOuterClass.HomeAvatarRewardEventGetRsp.newBuilder()
+                        .setEventId(eventId)
+                        .addAllItemList(rewards.stream().map(GameItem::toItemParam).toList()));
+    }
+
+    public PacketHomeAvatarRewardEventGetRsp(int eventId, int retcode) {
+        super(PacketOpcodes.HomeAvatarRewardEventGetRsp);
+
+        this.setData(
+                HomeAvatarRewardEventGetRspOuterClass.HomeAvatarRewardEventGetRsp.newBuilder()
+                        .setEventId(eventId)
+                        .setRetcode(retcode));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java
new file mode 100644
index 000000000..cea81c2ae
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java
@@ -0,0 +1,12 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+
+public class PacketHomeAvatarRewardEventNotify extends BasePacket {
+    public PacketHomeAvatarRewardEventNotify(Player homeOwner) {
+        super(PacketOpcodes.HomeAvatarRewardEventNotify);
+        this.setData(homeOwner.getCurHomeWorld().getModuleManager().toRewardEventProto());
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java
new file mode 100644
index 000000000..70925d38c
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java
@@ -0,0 +1,12 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.player.Player;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+
+public class PacketHomeAvatarSummonAllEventNotify extends BasePacket {
+    public PacketHomeAvatarSummonAllEventNotify(Player homeOwner) {
+        super(PacketOpcodes.HomeAvatarSummonAllEventNotify);
+        this.setData(homeOwner.getCurHomeWorld().getModuleManager().toSummonEventProto());
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
new file mode 100644
index 000000000..713e45574
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
@@ -0,0 +1,24 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarSummonEventRspOuterClass;
+
+public class PacketHomeAvatarSummonEventRsp extends BasePacket {
+    public PacketHomeAvatarSummonEventRsp(HomeAvatarSummonEvent event) {
+        super(PacketOpcodes.HomeAvatarSummonEventRsp);
+
+        this.setData(
+                HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder()
+                        .setEventId(event.getEventId()));
+    }
+
+    public PacketHomeAvatarSummonEventRsp(int retcode) {
+        super(PacketOpcodes.HomeAvatarSummonEventRsp);
+
+        this.setData(
+                HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder()
+                        .setRetcode(retcode));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
new file mode 100644
index 000000000..47be3d18c
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
@@ -0,0 +1,15 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.HomeAvatarSummonFinishRspOuterClass;
+
+public class PacketHomeAvatarSummonFinishRsp extends BasePacket {
+    public PacketHomeAvatarSummonFinishRsp(int eventId) {
+        super(PacketOpcodes.HomeAvatarSummonFinishRsp);
+
+        this.setData(
+                HomeAvatarSummonFinishRspOuterClass.HomeAvatarSummonFinishRsp.newBuilder()
+                        .setEventId(eventId));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
new file mode 100644
index 000000000..be2a61763
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
@@ -0,0 +1,15 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.PlayerQuitFromHomeNotifyOuterClass;
+
+public class PacketPlayerQuitFromHomeNotify extends BasePacket {
+    public PacketPlayerQuitFromHomeNotify(
+            PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason reason) {
+        super(PacketOpcodes.PlayerQuitFromHomeNotify);
+
+        this.setData(
+                PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.newBuilder().setReason(reason));
+    }
+}
diff --git a/src/main/java/emu/grasscutter/utils/Either.java b/src/main/java/emu/grasscutter/utils/Either.java
new file mode 100644
index 000000000..533340a3c
--- /dev/null
+++ b/src/main/java/emu/grasscutter/utils/Either.java
@@ -0,0 +1,155 @@
+package emu.grasscutter.utils;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public abstract class Either {
+    private static final class Left extends Either {
+        private final L value;
+
+        public Left(L value) {
+            this.value = value;
+        }
+
+        @Override
+        public  Either mapBoth(
+                Function f1, Function f2) {
+            return new Left<>(f1.apply(this.value));
+        }
+
+        @Override
+        public  T map(Function l, Function r) {
+            return l.apply(this.value);
+        }
+
+        @Override
+        public Either ifLeft(Consumer consumer) {
+            consumer.accept(this.value);
+            return this;
+        }
+
+        @Override
+        public Either ifRight(Consumer consumer) {
+            return this;
+        }
+
+        @Override
+        public Optional left() {
+            return Optional.of(this.value);
+        }
+
+        @Override
+        public Optional right() {
+            return Optional.empty();
+        }
+
+        @Override
+        public String toString() {
+            return "Left[" + this.value + "]";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Left left = (Left) o;
+            return Objects.equals(value, left.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(value);
+        }
+    }
+
+    private static final class Right extends Either {
+        private final R value;
+
+        public Right(R value) {
+            this.value = value;
+        }
+
+        @Override
+        public  Either mapBoth(
+                Function f1, Function f2) {
+            return new Right<>(f2.apply(this.value));
+        }
+
+        @Override
+        public  T map(Function l, Function r) {
+            return r.apply(this.value);
+        }
+
+        @Override
+        public Either ifLeft(Consumer consumer) {
+            return this;
+        }
+
+        @Override
+        public Either ifRight(Consumer consumer) {
+            consumer.accept(this.value);
+            return this;
+        }
+
+        @Override
+        public Optional left() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional right() {
+            return Optional.of(this.value);
+        }
+
+        @Override
+        public String toString() {
+            return "Right[" + this.value + "]";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Right right = (Right) o;
+            return Objects.equals(value, right.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(value);
+        }
+    }
+
+    private Either() {}
+
+    public abstract  Either mapBoth(
+            Function f1, Function f2);
+
+    public abstract  T map(Function l, Function r);
+
+    public abstract Either ifLeft(Consumer consumer);
+
+    public abstract Either ifRight(Consumer consumer);
+
+    public abstract Optional left();
+
+    public abstract Optional right();
+
+    public  Either mapLeft(Function l) {
+        return map(t -> left(l.apply(t)), Either::right);
+    }
+
+    public  Either mapRight(Function l) {
+        return map(Either::left, t -> right(l.apply(t)));
+    }
+
+    public static  Either left(L value) {
+        return new Left<>(value);
+    }
+
+    public static  Either right(R value) {
+        return new Right<>(value);
+    }
+}
diff --git a/src/main/resources/languages/ru-RU.json b/src/main/resources/languages/ru-RU.json
index 9828e1d6d..74f0c6417 100644
--- a/src/main/resources/languages/ru-RU.json
+++ b/src/main/resources/languages/ru-RU.json
@@ -260,7 +260,7 @@
       "refreshed": "Группа %s обновлена."
     },
     "cutscene": {
-      "description": "Odtwarza przerywnik filmowy"
+      "description": "Проигрывает кат-сцену"
     },
     "sound": {
       "description": "Проигрывает звук"
@@ -411,7 +411,7 @@
       "description": "Генерирует отладочную информацию для решения проблем."
     },
     "debug": {
-      "description": "🇺🇸Useful debugging commands for developers."
+      "description": "Полезные отладочные команды для разработкиков."
     }
   },
   "gacha": {
@@ -451,9 +451,9 @@
   },
   "plugin": {
     "directory_failed": "Ошибка создания директории плагинов: ",
-    "unable_to_load": "Невозможно загрудить плагин.",
+    "unable_to_load": "Невозможно загрузить плагин.",
     "invalid_config": "Плагин %s имеет недопустимый файл конфигурации.",
-    "invalid_main_class": "Плагин %s имеет неверный главный класс.",
+    "invalid_main_class": "Плагин %s имеет некорректный главный класс.",
     "missing_config": "Плагин %s не имеет корректного файла конфигурации.",
     "failed_to_load_plugin": "Ошибка загрузки плагина: %s",
     "failed_to_load": "Не удалось загрузить Плагин.",