From 83602f78ae271315ad06790c761a9445bd475daa Mon Sep 17 00:00:00 2001 From: Nazrin Date: Thu, 7 Sep 2023 20:33:13 -0700 Subject: [PATCH 01/13] Update readme to 4.0 from 3.7 (#2343) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bffa93df1..58789fdd1 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. From fc42f665a77863ac14b7b6db702245f563c97fc0 Mon Sep 17 00:00:00 2001 From: hamusuke Date: Fri, 8 Sep 2023 12:34:03 +0900 Subject: [PATCH 02/13] feat: implement teapot suite (#2344) * feat: implement teapot suite * fix: home animals, check respawn, etc * fix: NPE and cancel summon events * fix: forgot to send eventId also --- .../HomePlantSubFieldDataOuterClass.java | 248 +++++++++--------- .../java/emu/grasscutter/data/GameData.java | 4 + .../data/excels/HomeWorldEventData.java | 25 ++ .../game/entity/EntityHomeAnimal.java | 12 +- .../emu/grasscutter/game/home/GameHome.java | 61 ++++- .../grasscutter/game/home/HomeBlockItem.java | 20 +- .../game/home/HomeModuleManager.java | 204 ++++++++++++++ .../grasscutter/game/home/HomeNPCItem.java | 4 +- .../emu/grasscutter/game/home/HomeScene.java | 26 +- .../grasscutter/game/home/HomeSceneItem.java | 23 +- .../emu/grasscutter/game/home/HomeWorld.java | 50 +++- .../game/home/HomeWorldMPSystem.java | 7 +- .../game/home/suite/HomeSuiteItem.java | 82 ++++++ .../home/suite/event/HomeAvatarEvent.java | 54 ++++ .../suite/event/HomeAvatarRewardEvent.java | 37 +++ .../suite/event/HomeAvatarSummonEvent.java | 36 +++ .../game/home/suite/event/SuiteEventType.java | 19 ++ .../emu/grasscutter/game/player/Player.java | 7 + .../grasscutter/game/props/ActionReason.java | 3 +- .../emu/grasscutter/game/world/World.java | 2 +- .../HandlerHomeAvatarRewardEventGetReq.java | 30 +++ .../recv/HandlerHomeAvatarSummonEventReq.java | 19 ++ .../HandlerHomeAvatarSummonFinishReq.java | 21 ++ .../recv/HandlerHomeChangeEditModeReq.java | 11 +- .../recv/HandlerHomeChangeModuleReq.java | 18 +- .../HandlerHomeEnterEditModeFinishReq.java | 11 +- .../recv/HandlerHomeSceneInitFinishReq.java | 20 +- .../HandlerHomeUpdateArrangementInfoReq.java | 10 +- ...PacketHomeAvatarAllFinishRewardNotify.java | 18 ++ .../PacketHomeAvatarRewardEventGetRsp.java | 26 ++ .../PacketHomeAvatarRewardEventNotify.java | 12 + .../PacketHomeAvatarSummonAllEventNotify.java | 12 + .../send/PacketHomeAvatarSummonEventRsp.java | 22 ++ .../send/PacketHomeAvatarSummonFinishRsp.java | 14 + .../send/PacketPlayerQuitFromHomeNotify.java | 14 + .../java/emu/grasscutter/utils/Either.java | 153 +++++++++++ 36 files changed, 1125 insertions(+), 210 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java create mode 100644 src/main/java/emu/grasscutter/game/home/HomeModuleManager.java create mode 100644 src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java create mode 100644 src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java create mode 100644 src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java create mode 100644 src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java create mode 100644 src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java create mode 100644 src/main/java/emu/grasscutter/utils/Either.java 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..d5977c817
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
@@ -0,0 +1,25 @@
+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..c6652d967 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
@@ -10,12 +10,14 @@ import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
 import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
 import lombok.Getter;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
 public class EntityHomeAnimal extends EntityMonster implements Rebornable {
     private int rebornCDTickCount;
     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 +62,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 +76,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..fd5bcc893 100644
--- a/src/main/java/emu/grasscutter/game/home/GameHome.java
+++ b/src/main/java/emu/grasscutter/game/home/GameHome.java
@@ -12,15 +12,17 @@ import emu.grasscutter.game.props.SceneType;
 import emu.grasscutter.net.proto.HomeAvatarTalkFinishInfoOuterClass;
 import emu.grasscutter.server.packet.send.*;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Data;
-import lombok.experimental.FieldDefaults;
+import java.util.stream.Stream;
 
 @Entity(value = "homes", useDiscriminator = false)
 @Data
@@ -55,6 +57,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 +65,9 @@ public class GameHome {
             home = GameHome.create(uid);
         }
 
+        home.reassignIfNull();
         home.fixMainHouseIfOld();
+        home.syncHomeAvatarCostume();
 
         return home;
     }
@@ -79,9 +84,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 +112,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 +140,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 +176,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 +189,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 +257,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 +449,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 +461,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..3fa916e78 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,8 +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();
     }
 
     public int calComfort() {
@@ -81,15 +89,16 @@ 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 +116,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..d49f5650f
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
@@ -0,0 +1,204 @@
+package emu.grasscutter.game.home;
+
+import com.github.davidmoten.guavamini.Lists;
+import emu.grasscutter.Grasscutter;
+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 lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+@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() {
+        this.outdoor.onTick();
+        this.indoor.onTick();
+        this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver);
+    }
+
+    public void refreshMainHouse() {
+        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)
+            .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() {
+        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..3c6df4b22 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..21da833dd 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
@@ -6,17 +6,20 @@ 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 lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
+import javax.annotation.Nullable;
 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.experimental.FieldDefaults;
 
 @Entity
 @Data
@@ -49,14 +52,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 +87,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..8f871657b 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
@@ -8,33 +8,57 @@ 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 java.util.List;
+import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
 import lombok.Getter;
 
+import java.util.List;
+import java.util.function.Consumer;
+
 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() {
+        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 +212,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..f0da0bf86 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,7 @@ 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 +261,7 @@ 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..263240452
--- /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 lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+
+@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..60a4707f9
--- /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 lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import java.util.List;
+import java.util.Objects;
+
+@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..651212142
--- /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..961cfe7ab
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
@@ -0,0 +1,36 @@
+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..8c501f462
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
@@ -0,0 +1,19 @@
+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/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/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
new file mode 100644
index 000000000..6a5482edf
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
@@ -0,0 +1,30 @@
+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..c650e0110
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
@@ -0,0 +1,19 @@
+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..f18e5c141 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;
@@ -19,9 +17,10 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
     @Override
     public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
         HomeChangeModuleReqOuterClass.HomeChangeModuleReq req =
-                HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
+            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..a86e8f32c 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.*;
@@ -16,16 +18,20 @@ public class HandlerHomeSceneInitFinishReq extends PacketHandler {
             session.getPlayer().setHasSentInitPacketInHome(true);
 
             if (curHomeWorld.getHost().isOnline()
-                    && !curHomeWorld.getHost().equals(session.getPlayer())) {
+                && !curHomeWorld.getHost().equals(session.getPlayer())) {
                 curHomeWorld
-                        .getHost()
-                        .sendPacket(
-                                new PacketOtherPlayerEnterOrLeaveHomeNotify(
-                                        session.getPlayer(),
-                                        OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
+                    .getHost()
+                    .sendPacket(
+                        new PacketOtherPlayerEnterOrLeaveHomeNotify(
+                            session.getPlayer(),
+                            OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
             }
         }
 
+        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/send/PacketHomeAvatarAllFinishRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
new file mode 100644
index 000000000..eda4ae116
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
@@ -0,0 +1,18 @@
+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..73aa4d762
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
@@ -0,0 +1,26 @@
+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..6c28fb695
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
@@ -0,0 +1,22 @@
+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..66ad749e4
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
@@ -0,0 +1,14 @@
+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..2c5a82332
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
@@ -0,0 +1,14 @@
+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..b976fdfbd
--- /dev/null
+++ b/src/main/java/emu/grasscutter/utils/Either.java
@@ -0,0 +1,153 @@
+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);
+    }
+}

From cdcdf924bd6737a47d26d90821d48edae11b5a22 Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 8 Sep 2023 03:35:28 +0000
Subject: [PATCH 03/13] Format code [skip actions]

---
 .../data/excels/HomeWorldEventData.java       |  21 +++-
 .../game/entity/EntityHomeAnimal.java         |   3 +-
 .../emu/grasscutter/game/home/GameHome.java   |  33 +++--
 .../grasscutter/game/home/HomeBlockItem.java  |  13 +-
 .../game/home/HomeModuleManager.java          | 116 ++++++++++--------
 .../emu/grasscutter/game/home/HomeScene.java  |   8 +-
 .../grasscutter/game/home/HomeSceneItem.java  |  11 +-
 .../emu/grasscutter/game/home/HomeWorld.java  |   3 +-
 .../game/home/HomeWorldMPSystem.java          |   9 +-
 .../game/home/suite/HomeSuiteItem.java        |  52 ++++----
 .../home/suite/event/HomeAvatarEvent.java     |   8 +-
 .../suite/event/HomeAvatarRewardEvent.java    |  16 +--
 .../suite/event/HomeAvatarSummonEvent.java    |  17 +--
 .../game/home/suite/event/SuiteEventType.java |  14 ++-
 .../HandlerHomeAvatarRewardEventGetReq.java   |  11 +-
 .../recv/HandlerHomeAvatarSummonEventReq.java |   7 +-
 .../recv/HandlerHomeChangeModuleReq.java      |   2 +-
 .../recv/HandlerHomeSceneInitFinishReq.java   |  22 ++--
 ...PacketHomeAvatarAllFinishRewardNotify.java |   5 +-
 .../PacketHomeAvatarRewardEventGetRsp.java    |  15 +--
 .../send/PacketHomeAvatarSummonEventRsp.java  |  10 +-
 .../send/PacketHomeAvatarSummonFinishRsp.java |   5 +-
 .../send/PacketPlayerQuitFromHomeNotify.java  |   7 +-
 .../java/emu/grasscutter/utils/Either.java    |  12 +-
 24 files changed, 236 insertions(+), 184 deletions(-)

diff --git a/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
index d5977c817..704ef73ce 100644
--- a/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
+++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
@@ -12,14 +12,27 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level = AccessLevel.PRIVATE)
 @Getter
 public class HomeWorldEventData extends GameResource {
-    @SerializedName(value = "id", alternate = {"BBEIIPEFDPE"})
+    @SerializedName(
+            value = "id",
+            alternate = {"BBEIIPEFDPE"})
     int id;
-    @SerializedName(value = "eventType", alternate = {"JOCKIMECHDP"})
+
+    @SerializedName(
+            value = "eventType",
+            alternate = {"JOCKIMECHDP"})
     SuiteEventType eventType;
+
     int avatarID;
-    @SerializedName(value = "talkId", alternate = {"IGNJAICDFPD"})
+
+    @SerializedName(
+            value = "talkId",
+            alternate = {"IGNJAICDFPD"})
     int talkId;
+
     int rewardID;
-    @SerializedName(value = "suiteId", alternate = {"FEHOKMJPOED"})
+
+    @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 c6652d967..40e61c339 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
@@ -8,9 +8,8 @@ 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 lombok.Getter;
-
 import java.util.concurrent.atomic.AtomicBoolean;
+import lombok.Getter;
 
 public class EntityHomeAnimal extends EntityMonster implements Rebornable {
     private int rebornCDTickCount;
diff --git a/src/main/java/emu/grasscutter/game/home/GameHome.java b/src/main/java/emu/grasscutter/game/home/GameHome.java
index fd5bcc893..d4a2348c5 100644
--- a/src/main/java/emu/grasscutter/game/home/GameHome.java
+++ b/src/main/java/emu/grasscutter/game/home/GameHome.java
@@ -12,17 +12,16 @@ import emu.grasscutter.game.props.SceneType;
 import emu.grasscutter.net.proto.HomeAvatarTalkFinishInfoOuterClass;
 import emu.grasscutter.server.packet.send.*;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Data;
-import lombok.experimental.FieldDefaults;
-
 import java.time.ZonedDateTime;
 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;
+import lombok.experimental.FieldDefaults;
 
 @Entity(value = "homes", useDiscriminator = false)
 @Data
@@ -91,10 +90,10 @@ public class GameHome {
     // 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);
+                .map(HomeSceneItem::getBlockItems)
+                .map(Map::values)
+                .flatMap(Collection::stream)
+                .forEach(HomeBlockItem::reassignIfNull);
     }
 
     // Data fixer.
@@ -114,14 +113,14 @@ public class GameHome {
 
     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())));
+                .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() {
diff --git a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
index 3fa916e78..69fe9f50a 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
@@ -67,9 +67,10 @@ public class HomeBlockItem {
                         .map(homeNpcData -> HomeNPCItem.parseFrom(homeNpcData, owner))
                         .toList();
 
-        this.suiteList = homeBlockArrangementInfo.getFurnitureSuiteListList().stream()
-            .map(HomeSuiteItem::parseFrom)
-            .toList();
+        this.suiteList =
+                homeBlockArrangementInfo.getFurnitureSuiteListList().stream()
+                        .map(HomeSuiteItem::parseFrom)
+                        .toList();
     }
 
     public int calComfort() {
@@ -98,7 +99,11 @@ public class HomeBlockItem {
     public List getMarkPointProtoFactories() {
         this.reassignIfNull();
 
-        return Stream.of(this.deployFurnitureList, this.persistentFurnitureList, this.deployNPCList, this.suiteList)
+        return Stream.of(
+                        this.deployFurnitureList,
+                        this.persistentFurnitureList,
+                        this.deployNPCList,
+                        this.suiteList)
                 .flatMap(Collection::stream)
                 .toList();
     }
diff --git a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
index d49f5650f..c739c077e 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
@@ -1,7 +1,6 @@
 package emu.grasscutter.game.home;
 
 import com.github.davidmoten.guavamini.Lists;
-import emu.grasscutter.Grasscutter;
 import emu.grasscutter.game.home.suite.event.HomeAvatarRewardEvent;
 import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent;
 import emu.grasscutter.game.home.suite.event.SuiteEventType;
@@ -12,13 +11,12 @@ 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;
 
-import java.util.*;
-import java.util.stream.Stream;
-
 @Getter
 @FieldDefaults(level = AccessLevel.PRIVATE)
 public class HomeModuleManager {
@@ -59,31 +57,44 @@ public class HomeModuleManager {
 
     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 allBlockItems =
+                Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem())
+                        .map(HomeSceneItem::getBlockItems)
+                        .map(Map::values)
+                        .flatMap(Collection::stream)
+                        .toList();
 
-        var suites = allBlockItems.stream()
-            .map(HomeBlockItem::getSuiteList)
-            .flatMap(Collection::stream)
-            .distinct()
-            .toList();
+        var suites =
+                allBlockItems.stream()
+                        .map(HomeBlockItem::getSuiteList)
+                        .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;
-                    }
+                .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()));
-                });
-            });
+                                        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();
@@ -92,14 +103,15 @@ public class HomeModuleManager {
     }
 
     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();
+        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()));
     }
@@ -121,13 +133,16 @@ public class HomeModuleManager {
         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);
+    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);
@@ -148,7 +163,9 @@ public class HomeModuleManager {
             return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE);
         }
 
-        var event = new HomeAvatarSummonEvent(owner, eventData.getId(), eventData.getRewardID(), avatarId, suiteId, guid);
+        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);
@@ -163,20 +180,21 @@ public class HomeModuleManager {
         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());
+            notify.addAllPendingList(
+                    this.rewardEvents.subList(1, this.rewardEvents.size()).stream()
+                            .map(HomeAvatarRewardEvent::toProto)
+                            .toList());
         }
 
         return notify.build();
     }
 
-    public HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify toSummonEventProto() {
+    public HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify
+            toSummonEventProto() {
         return HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify.newBuilder()
-            .addAllSummonEventList(this.summonEvents.stream()
-                .map(HomeAvatarSummonEvent::toProto)
-                .toList())
-            .build();
+                .addAllSummonEventList(
+                        this.summonEvents.stream().map(HomeAvatarSummonEvent::toProto).toList())
+                .build();
     }
 
     public boolean isInRewardEvent(int avatarId) {
diff --git a/src/main/java/emu/grasscutter/game/home/HomeScene.java b/src/main/java/emu/grasscutter/game/home/HomeScene.java
index 3c6df4b22..c03247c1d 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeScene.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeScene.java
@@ -48,10 +48,10 @@ public class HomeScene extends Scene {
 
     public void onEnterEditModeFinish() {
         this.removeEntities(
-            this.getEntities().values().stream()
-                .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal)
-                .toList(),
-            VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
+                this.getEntities().values().stream()
+                        .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal)
+                        .toList(),
+                VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
     }
 
     public void onLeaveEditMode() {
diff --git a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
index 21da833dd..93031537c 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
@@ -10,16 +10,15 @@ 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 lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Data;
-import lombok.experimental.FieldDefaults;
-
-import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
 
 @Entity
 @Data
diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorld.java b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
index 8f871657b..73718925f 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
@@ -11,10 +11,9 @@ import emu.grasscutter.server.game.GameServer;
 import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
 import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
 import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
-import lombok.Getter;
-
 import java.util.List;
 import java.util.function.Consumer;
+import lombok.Getter;
 
 public class HomeWorld extends World {
     @Getter private final GameHome home;
diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
index f0da0bf86..0835975b0 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java
@@ -212,7 +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 PacketPlayerQuitFromHomeNotify(
+                        PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason
+                                .BACK_TO_MY_WORLD));
         player.sendPacket(
                 new PacketPlayerEnterSceneNotify(
                         player,
@@ -261,7 +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 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
index 263240452..88841e9fb 100644
--- a/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java
+++ b/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java
@@ -7,15 +7,14 @@ 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;
 
-import java.util.List;
-import java.util.Objects;
-
 @Entity
 @Builder(builderMethodName = "of")
 @Getter
@@ -28,38 +27,39 @@ public class HomeSuiteItem implements HomeMarkPointProtoFactory {
     List includedFurnitureIndexList;
     boolean isAllowSummon;
 
-    public static HomeSuiteItem parseFrom(HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData data) {
+    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();
+                .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();
+                .setSuiteId(this.suiteId)
+                .setGuid(this.guid)
+                .setIsAllowSummon(this.isAllowSummon)
+                .addAllIncludedFurnitureIndexList(this.includedFurnitureIndexList)
+                .setSpawnPos(this.pos.toProto())
+                .build();
     }
 
-    @Nullable
-    @Override
+    @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();
+                .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
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
index 60a4707f9..06d2fc2a9 100644
--- a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java
@@ -3,13 +3,12 @@ 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;
 
-import java.util.List;
-import java.util.Objects;
-
 @Getter
 @FieldDefaults(level = AccessLevel.PRIVATE)
 public abstract class HomeAvatarEvent {
@@ -21,7 +20,8 @@ public abstract class HomeAvatarEvent {
     final int guid;
     final int randomPos;
 
-    public HomeAvatarEvent(Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) {
+    public HomeAvatarEvent(
+            Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) {
         this.homeOwner = homeOwner;
         this.eventId = eventId;
         this.rewardId = rewardId;
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
index 651212142..de897faf0 100644
--- a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java
@@ -5,22 +5,22 @@ 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) {
+    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();
+                .setAvatarId(this.getAvatarId())
+                .setEventId(this.getEventId())
+                .setGuid(this.getGuid())
+                .setSuiteId(this.getSuiteId())
+                .setRandomPosition(this.getRandomPos())
+                .build();
     }
 
     @Override
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
index 961cfe7ab..f6b9c0b5e 100644
--- a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
@@ -13,7 +13,8 @@ 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) {
+    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;
@@ -21,13 +22,13 @@ public class HomeAvatarSummonEvent extends HomeAvatarEvent {
 
     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();
+                .setAvatarId(this.getAvatarId())
+                .setEventId(this.getEventId())
+                .setGuid(this.getGuid())
+                .setSuitId(this.getSuiteId())
+                .setRandomPosition(this.getRandomPos())
+                .setEventOverTime(this.eventOverTime)
+                .build();
     }
 
     public boolean isTimeOver() {
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
index 8c501f462..6056744e8 100644
--- a/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
@@ -2,18 +2,20 @@ 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) {
+    @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);
+                .filter(
+                        data ->
+                                data.getEventType() == this
+                                        && data.getAvatarID() == avatarId
+                                        && data.getSuiteId() == suiteId)
+                .findFirst()
+                .orElse(null);
     }
 }
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
index 6a5482edf..6b616795b 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
@@ -16,15 +16,14 @@ public class HandlerHomeAvatarRewardEventGetReq extends PacketHandler {
         var req = HomeAvatarRewardEventGetReqOuterClass.HomeAvatarRewardEventGetReq.parseFrom(payload);
 
         var player = session.getPlayer();
-        var rewardsOrError = player.getCurHomeWorld().getModuleManager().claimAvatarRewards(req.getEventId());
+        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)
-            )
-        );
+                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
index c650e0110..ada4476b3 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
@@ -13,7 +13,10 @@ public class HandlerHomeAvatarSummonEventReq extends PacketHandler {
     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));
+        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/HandlerHomeChangeModuleReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
index f18e5c141..e05831d40 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java
@@ -17,7 +17,7 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
     @Override
     public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
         HomeChangeModuleReqOuterClass.HomeChangeModuleReq req =
-            HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
+                HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
 
         var homeWorld = session.getPlayer().getCurHomeWorld();
         if (!homeWorld.getGuests().isEmpty()) {
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 a86e8f32c..4f7e972c7 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java
@@ -18,20 +18,22 @@ public class HandlerHomeSceneInitFinishReq extends PacketHandler {
             session.getPlayer().setHasSentInitPacketInHome(true);
 
             if (curHomeWorld.getHost().isOnline()
-                && !curHomeWorld.getHost().equals(session.getPlayer())) {
+                    && !curHomeWorld.getHost().equals(session.getPlayer())) {
                 curHomeWorld
-                    .getHost()
-                    .sendPacket(
-                        new PacketOtherPlayerEnterOrLeaveHomeNotify(
-                            session.getPlayer(),
-                            OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
+                        .getHost()
+                        .sendPacket(
+                                new PacketOtherPlayerEnterOrLeaveHomeNotify(
+                                        session.getPlayer(),
+                                        OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
             }
         }
 
-        curHomeWorld.ifHost(session.getPlayer(), player -> {
-            player.sendPacket(new PacketHomeAvatarRewardEventNotify(player));
-            player.sendPacket(new PacketHomeAvatarSummonAllEventNotify(player));
-        });
+        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/send/PacketHomeAvatarAllFinishRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
index eda4ae116..df07429f9 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
@@ -11,8 +11,9 @@ public class PacketHomeAvatarAllFinishRewardNotify extends BasePacket {
 
         var list = player.getHome().getFinishedRewardEventIdSet();
         if (list != null) {
-            this.setData(HomeAvatarAllFinishRewardNotifyOuterClass.HomeAvatarAllFinishRewardNotify.newBuilder()
-                .addAllEventIdList(player.getHome().getFinishedRewardEventIdSet()));
+            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
index 73aa4d762..8bff115ec 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
@@ -4,23 +4,24 @@ 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()));
+        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));
+        this.setData(
+                HomeAvatarRewardEventGetRspOuterClass.HomeAvatarRewardEventGetRsp.newBuilder()
+                        .setEventId(eventId)
+                        .setRetcode(retcode));
     }
 }
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
index 6c28fb695..713e45574 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
@@ -9,14 +9,16 @@ public class PacketHomeAvatarSummonEventRsp extends BasePacket {
     public PacketHomeAvatarSummonEventRsp(HomeAvatarSummonEvent event) {
         super(PacketOpcodes.HomeAvatarSummonEventRsp);
 
-        this.setData(HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder()
-            .setEventId(event.getEventId()));
+        this.setData(
+                HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder()
+                        .setEventId(event.getEventId()));
     }
 
     public PacketHomeAvatarSummonEventRsp(int retcode) {
         super(PacketOpcodes.HomeAvatarSummonEventRsp);
 
-        this.setData(HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder()
-            .setRetcode(retcode));
+        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
index 66ad749e4..47be3d18c 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
@@ -8,7 +8,8 @@ public class PacketHomeAvatarSummonFinishRsp extends BasePacket {
     public PacketHomeAvatarSummonFinishRsp(int eventId) {
         super(PacketOpcodes.HomeAvatarSummonFinishRsp);
 
-        this.setData(HomeAvatarSummonFinishRspOuterClass.HomeAvatarSummonFinishRsp.newBuilder()
-            .setEventId(eventId));
+        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
index 2c5a82332..be2a61763 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
@@ -5,10 +5,11 @@ import emu.grasscutter.net.packet.PacketOpcodes;
 import emu.grasscutter.net.proto.PlayerQuitFromHomeNotifyOuterClass;
 
 public class PacketPlayerQuitFromHomeNotify extends BasePacket {
-    public PacketPlayerQuitFromHomeNotify(PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason reason) {
+    public PacketPlayerQuitFromHomeNotify(
+            PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason reason) {
         super(PacketOpcodes.PlayerQuitFromHomeNotify);
 
-        this.setData(PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.newBuilder()
-            .setReason(reason));
+        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
index b976fdfbd..533340a3c 100644
--- a/src/main/java/emu/grasscutter/utils/Either.java
+++ b/src/main/java/emu/grasscutter/utils/Either.java
@@ -14,7 +14,8 @@ public abstract class Either {
         }
 
         @Override
-        public  Either mapBoth(Function f1, Function f2) {
+        public  Either mapBoth(
+                Function f1, Function f2) {
             return new Left<>(f1.apply(this.value));
         }
 
@@ -71,7 +72,8 @@ public abstract class Either {
         }
 
         @Override
-        public  Either mapBoth(Function f1, Function f2) {
+        public  Either mapBoth(
+                Function f1, Function f2) {
             return new Right<>(f2.apply(this.value));
         }
 
@@ -120,10 +122,10 @@ public abstract class Either {
         }
     }
 
-    private Either() {
-    }
+    private Either() {}
 
-    public abstract  Either mapBoth(Function f1, Function f2);
+    public abstract  Either mapBoth(
+            Function f1, Function f2);
 
     public abstract  T map(Function l, Function r);
 

From a9402f487f9dac137069440de7b4ae399a1ff748 Mon Sep 17 00:00:00 2001
From: Nazrin 
Date: Fri, 8 Sep 2023 20:06:14 -0700
Subject: [PATCH 04/13] Implement RegionShape.POLYGON and RegionShape.CYLINDER
 (#2348)

Also took the opportunity to sort them in order and use multiplication instead of pow.
---
 .../grasscutter/scripts/data/SceneRegion.java | 50 +++++++++++++++----
 1 file changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
index 77706c7ec..99db23615 100644
--- a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
+++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
@@ -30,16 +30,48 @@ public class SceneRegion {
 
     public boolean contains(Position position) {
         switch (shape) {
-            case ScriptRegionShape.CUBIC:
-                return (Math.abs(pos.getX() - position.getX()) <= size.getX() / 2f)
-                        && (Math.abs(pos.getY() - position.getY()) <= size.getY() / 2f)
+            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;
     }

From 818b638bed2d54a9f3afeaeb3b59eb3de50b11d5 Mon Sep 17 00:00:00 2001
From: Pradeep 
Date: Sat, 9 Sep 2023 08:37:18 +0530
Subject: [PATCH 05/13]  Added Hindi Language Support  (#2347)

* Added Hindi Language Support

Please review the code

* Update README_HE.md

* Update README_NL.md

* Update README_es-ES.md

* Update README_fil-PH.md

* Update README_fr-FR.md

* Update README_id-ID.md

* Update README_it-IT.md

* Update README_ja-JP.md

* Update README_ko-KR.md

* Update README_pl-PL.md

* Update README_ru-RU.md

* Update README_vi-VN.md

* Update README_zh-CN.md

* Update README_zh-TW.md
---
 docs/README_HE.md     |  2 +-
 docs/README_NL.md     |  2 +-
 docs/README_es-ES.md  |  2 +-
 docs/README_fil-PH.md |  2 +-
 docs/README_fr-FR.md  |  4 +--
 docs/README_hn-IN.md  | 78 +++++++++++++++++++++++++++++++++++++++++++
 docs/README_id-ID.md  |  2 +-
 docs/README_it-IT.md  |  2 +-
 docs/README_ja-JP.md  |  2 +-
 docs/README_ko-KR.md  |  2 +-
 docs/README_pl-PL.md  |  2 +-
 docs/README_ru-RU.md  |  2 +-
 docs/README_vi-VN.md  |  2 +-
 docs/README_zh-CN.md  |  2 +-
 docs/README_zh-TW.md  |  2 +-
 15 files changed, 93 insertions(+), 15 deletions(-)
 create mode 100644 docs/README_hn-IN.md

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..4a003e45c 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)。 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)。 From 7845c545700fb124c851dfe2042fa88efc5cc68a Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Sep 2023 03:07:29 +0000 Subject: [PATCH 06/13] Format code [skip actions] --- .../java/emu/grasscutter/scripts/data/SceneRegion.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java index 99db23615..ea4b2ece3 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java @@ -38,8 +38,8 @@ public class SceneRegion { 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) + 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.POLYGON -> { @@ -57,8 +57,7 @@ public class SceneRegion { 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)) { + if ((yp < y1) != (yp < y2) && xp < x1 + ((yp - y1) / (y2 - y1)) * (x2 - x1)) { ++count; } } From 04370f1a210459f65b3cd296c6860958a65177dc Mon Sep 17 00:00:00 2001 From: CJYKK <59359590+CJYKK@users.noreply.github.com> Date: Sun, 10 Sep 2023 08:48:32 +0800 Subject: [PATCH 07/13] Update CN README to game REL 4.0 (#2350) --- docs/README_zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README_zh-CN.md b/docs/README_zh-CN.md index 4a003e45c..76fd11dee 100644 --- a/docs/README_zh-CN.md +++ b/docs/README_zh-CN.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,按右上角的下载按钮。 From 47c96fd96445749220a940071b23be56d3daf708 Mon Sep 17 00:00:00 2001 From: Nazrin Date: Sat, 9 Sep 2023 17:49:22 -0700 Subject: [PATCH 08/13] Regular ScriptLib update! (#2349) * Regular ScriptLib update! * Move RefreshGroup down to debug * Update SetGadgetEnableInteract --- .../emu/grasscutter/scripts/ScriptLib.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) 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; From fbe2b138ee32bd58ef07bf11c9f29989634d2adf Mon Sep 17 00:00:00 2001 From: Nazrin Date: Sun, 10 Sep 2023 16:20:28 -0700 Subject: [PATCH 09/13] Fixes for alchemy dude's quest (#2352) * Add drops for gadgets Gadgets only have drop_id when they are not chests (chest_drop_id). When drop_id is not set (0), handleChestDrop quickly exits * Implement QUEST_COND_ITEM_GIVING_FINISHED Took the oppertunity to Rename ContentFinishGivingItem to ItemGiving * Store accept conditions like fail and finish content are Took the oppertunity to clean up some old code as well conditions are stored in QuestManager * Update src/main/java/emu/grasscutter/game/quest/QuestManager.java Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com> * Update ConditionItemGivingFinished.java --------- Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com> --- .../grasscutter/game/quest/GameMainQuest.java | 61 +------------------ .../emu/grasscutter/game/quest/GameQuest.java | 4 ++ .../grasscutter/game/quest/QuestManager.java | 22 ++++--- .../ConditionItemGivingFinished.java | 20 ++++++ ...Item.java => ContentFinishItemGiving.java} | 4 +- .../game/quest/enums/QuestCond.java | 2 +- .../emu/grasscutter/game/world/Scene.java | 8 ++- .../grasscutter/scripts/data/SceneGadget.java | 1 + .../packet/recv/HandlerItemGivingReq.java | 9 ++- 9 files changed, 55 insertions(+), 76 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java rename src/main/java/emu/grasscutter/game/quest/content/{ContentFinishGivingItem.java => ContentFinishItemGiving.java} (68%) 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..3cfd362a2 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -158,6 +158,10 @@ 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..16a3b1188 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,17 @@ 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 +524,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 +534,14 @@ 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); + .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..37d543b42 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java @@ -0,0 +1,20 @@ +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 68% 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..b73626b7d 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,10 @@ 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..ea3d186b1 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -535,7 +535,13 @@ 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/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/server/packet/recv/HandlerItemGivingReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java index 15b73fcf0..ebf368885 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,9 @@ 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)); } } } From 8de281d4da2119c67b7ac91693ca6f99a6ee8de2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:21:44 +0000 Subject: [PATCH 10/13] Format code [skip actions] --- src/main/java/emu/grasscutter/game/quest/GameQuest.java | 5 ++++- src/main/java/emu/grasscutter/game/quest/QuestManager.java | 7 ++++--- .../game/quest/conditions/ConditionItemGivingFinished.java | 3 ++- .../game/quest/content/ContentFinishItemGiving.java | 3 ++- src/main/java/emu/grasscutter/game/world/Scene.java | 6 +++++- .../server/packet/recv/HandlerItemGivingReq.java | 6 ++++-- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java index 3cfd362a2..7888e4701 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -159,7 +159,10 @@ public class GameQuest { // 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()]); + this.getMainQuest() + .getQuestManager() + .getAcceptProgressLists() + .put(this.getSubQuestId(), new int[questData.getAcceptCond().size()]); } if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) { diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index 16a3b1188..a14350327 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -512,7 +512,9 @@ public final class QuestManager extends BasePlayerManager { } } - boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), acceptProgressLists.get(questData.getId())); + boolean shouldAccept = + LogicType.calculate( + questData.getAcceptCondComb(), acceptProgressLists.get(questData.getId())); if (this.loggedQuests.contains(questData.getId())) { Grasscutter.getLogger() .debug( @@ -540,8 +542,7 @@ public final class QuestManager extends BasePlayerManager { if (shouldAccept) { GameQuest quest = owner.getQuestManager().addQuest(questData); - Grasscutter.getLogger() - .debug("Added quest {}", questData.getSubId()); + 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 index 37d543b42..fe77519cb 100644 --- a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java @@ -15,6 +15,7 @@ public class ConditionItemGivingFinished extends BaseCondition { QuestData.QuestAcceptCondition condition, String paramStr, int... params) { - return condition.getParam()[0] == params[0] && (condition.getParam()[1] == 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/content/ContentFinishItemGiving.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java index b73626b7d..01e3171b7 100644 --- a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java @@ -9,6 +9,7 @@ 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] == 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/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index ea3d186b1..f9c629031 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -541,7 +541,11 @@ public class Scene { if (target instanceof EntityGadget gadget) { if (gadget.getMetaGadget() != null) { - world.getServer().getDropSystem().handleChestDrop(gadget.getMetaGadget().drop_id, gadget.getMetaGadget().drop_count, gadget); + world + .getServer() + .getDropSystem() + .handleChestDrop( + gadget.getMetaGadget().drop_id, gadget.getMetaGadget().drop_count, gadget); } } 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 ebf368885..23db1bfb8 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java @@ -99,8 +99,10 @@ public final class HandlerItemGivingReq extends PacketHandler { // Mark the giving action as completed. questManager.markCompleted(giveId); // 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)); + questManager.queueEvent( + QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, matchedGroups.get(0)); + questManager.queueEvent( + QuestCond.QUEST_COND_ITEM_GIVING_FINISHED, giveId, matchedGroups.get(0)); } } } From 5fd31aece807dc39e496eb066fe691810d76bcd0 Mon Sep 17 00:00:00 2001 From: Thoronium <107363768+NotThorny@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:07:17 -0600 Subject: [PATCH 11/13] Fix home nulls (#2355) * Add null checking * Sanity check for moduleManager --- .../grasscutter/game/home/HomeModuleManager.java | 13 +++++++++++++ .../java/emu/grasscutter/game/home/HomeWorld.java | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java index c739c077e..26460c09b 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java +++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java @@ -41,12 +41,20 @@ public class HomeModuleManager { } 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()); } @@ -67,6 +75,7 @@ public class HomeModuleManager { var suites = allBlockItems.stream() .map(HomeBlockItem::getSuiteList) + .filter(Objects::nonNull) .flatMap(Collection::stream) .distinct() .toList(); @@ -210,6 +219,10 @@ public class HomeModuleManager { } 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(); diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorld.java b/src/main/java/emu/grasscutter/game/home/HomeWorld.java index 73718925f..525da1be4 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java +++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java @@ -29,6 +29,9 @@ public class HomeWorld extends World { @Override public boolean onTick() { + if (this.moduleManager == null) { + return false; + } this.moduleManager.tick(); if (this.getTickCount() % 10 == 0) { From 4f62e484adc2218c11f7126158acf7e672aa923d Mon Sep 17 00:00:00 2001 From: TotallyNotOndre <40639199+TotallyNotOndre@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:56:31 +0300 Subject: [PATCH 12/13] Update ru-RU.json (#2356) translation & typo --- src/main/resources/languages/ru-RU.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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": "Не удалось загрузить Плагин.", From 98a83b649e3191baa1a6452ebfb6e6e3a5504d67 Mon Sep 17 00:00:00 2001 From: Ceris White <60857285+CerisWhite@users.noreply.github.com> Date: Wed, 13 Sep 2023 20:02:20 -0500 Subject: [PATCH 13/13] Add more precise instructions for the handbook (#2359) * Add more precise instructions for the handbook * Reformat Building section --- README.md | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 58789fdd1..67cd2e116 100644 --- a/README.md +++ b/README.md @@ -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.