diff --git a/build.gradle b/build.gradle index d9f616ca9..70f6a9358 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,9 @@ dependencies { implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1' implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9' + implementation group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2' + implementation group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.3.2' + protobuf files('proto/') } diff --git a/proto/CardProductRewardNotify.proto b/proto/CardProductRewardNotify.proto new file mode 100644 index 000000000..109e685e1 --- /dev/null +++ b/proto/CardProductRewardNotify.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +message CardProductRewardNotify { + enum CmdId { + option allow_alias = true; + NONE = 0; + ENET_CHANNEL_ID = 0; + ENET_IS_RELIABLE = 1; + CMD_ID = 4105; + } + + string product_id = 1; + uint32 hcoin = 2; + uint32 remain_days = 3; +} diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index eac10a0cc..4298f3bed 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -81,6 +81,11 @@ public class GenshinPlayer { private int mainCharacterId; private boolean godmode; + private boolean moonCard; + private Date moonCardStartTime; + private int moonCardDuration; + private Set moonCardGetTimes; + @Transient private boolean paused; @Transient private int enterSceneToken; @Transient private SceneLoadState sceneState; @@ -124,6 +129,7 @@ public class GenshinPlayer { this.birthday = new PlayerBirthday(); this.rewardedLevels = new HashSet<>(); + this.moonCardGetTimes = new HashSet<>(); } // On player creation @@ -486,6 +492,95 @@ public class GenshinPlayer { this.regionId = regionId; } + public boolean inMoonCard() { + return moonCard; + } + + public void setMoonCard(boolean moonCard) { + this.moonCard = moonCard; + } + + public void addMoonCardDays(int days) { + this.moonCardDuration += days; + } + + public int getMoonCardDuration() { + return moonCardDuration; + } + + public void setMoonCardDuration(int moonCardDuration) { + this.moonCardDuration = moonCardDuration; + } + + public Date getMoonCardStartTime() { + return moonCardStartTime; + } + + public void setMoonCardStartTime(Date moonCardStartTime) { + this.moonCardStartTime = moonCardStartTime; + } + + public Set getMoonCardGetTimes() { + return moonCardGetTimes; + } + + public void setMoonCardGetTimes(Set moonCardGetTimes) { + this.moonCardGetTimes = moonCardGetTimes; + } + + public int getMoonCardRemainDays() { + Calendar remainCalendar = Calendar.getInstance(); + remainCalendar.setTime(moonCardStartTime); + remainCalendar.add(Calendar.DATE, moonCardDuration); + Date theLastDay = remainCalendar.getTime(); + Date now = DateHelper.onlyYDM(new Date()); + return (int) ((theLastDay.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)); // By copilot + } + + public void rechargeMoonCard() { + LinkedList items = new LinkedList(); + for (int i = 0; i < 300; i++) { + items.add(new GenshinItem(203)); + } + inventory.addItems(items); + if (!moonCard) { + moonCard = true; + Date now = new Date(); + moonCardStartTime = DateHelper.onlyYDM(now); + moonCardDuration = 30; + } else { + moonCardDuration += 30; + } + if (!moonCardGetTimes.contains(moonCardStartTime)) { + moonCardGetTimes.add(moonCardStartTime); + } + } + + public void getTodayMoonCard() { + if (!moonCard) { + return; + } + Date now = DateHelper.onlyYDM(new Date()); + if (moonCardGetTimes.contains(now)) { + return; + } + Date stopTime = new Date(); + Calendar stopCalendar = Calendar.getInstance(); + stopCalendar.setTime(stopTime); + stopCalendar.add(Calendar.DATE, moonCardDuration); + stopTime = stopCalendar.getTime(); + if (now.after(stopTime)) { + moonCard = false; + return; + } + moonCardGetTimes.add(now); + addMoonCardDays(1); + GenshinItem genshinItem = new GenshinItem(201, 90); + getInventory().addItem(genshinItem); + session.send(new PacketItemAddHintNotify(genshinItem, ActionReason.BlessingRedeemReward)); + session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays())); + } + public boolean inGodmode() { return godmode; } diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 4ea9e6bc1..4b71db904 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -922,6 +922,11 @@ public class InventoryManager { default: break; } + + if (useItem.getItemId() == 1202) { + player.rechargeMoonCard(); + used = 1; + } if (used > 0) { player.getInventory().removeItem(useItem, used); diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b37b24f33..77f64fb88 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -25,6 +25,7 @@ import emu.grasscutter.server.event.ServerEvent; import emu.grasscutter.server.event.game.ServerTickEvent; import emu.grasscutter.server.event.internal.ServerStartEvent; import emu.grasscutter.server.event.internal.ServerStopEvent; +import emu.grasscutter.task.TaskMap; public final class GameServer extends MihoyoKcpServer { private final InetSocketAddress address; @@ -40,6 +41,7 @@ public final class GameServer extends MihoyoKcpServer { private final MultiplayerManager multiplayerManager; private final DungeonManager dungeonManager; private final CommandMap commandMap; + private final TaskMap taskMap; public GameServer(InetSocketAddress address) { super(address); @@ -57,6 +59,7 @@ public final class GameServer extends MihoyoKcpServer { this.multiplayerManager = new MultiplayerManager(this); this.dungeonManager = new DungeonManager(this); this.commandMap = new CommandMap(true); + this.taskMap = new TaskMap(true); // Schedule game loop. Timer gameLoop = new Timer(); @@ -114,6 +117,10 @@ public final class GameServer extends MihoyoKcpServer { public CommandMap getCommandMap() { return this.commandMap; } + + public TaskMap getTaskMap() { + return this.taskMap; + } public void registerPlayer(GenshinPlayer player) { getPlayers().put(player.getUid(), player); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCardProductRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCardProductRewardNotify.java new file mode 100644 index 000000000..d1079a42d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCardProductRewardNotify.java @@ -0,0 +1,892 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: CardProductRewardNotify.proto + +package emu.grasscutter.net.proto; + +public final class CardProductRewardNotifyOuterClass { + private CardProductRewardNotifyOuterClass() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface CardProductRewardNotifyOrBuilder extends + // @@protoc_insertion_point(interface_extends:CardProductRewardNotify) + com.google.protobuf.MessageOrBuilder { + + /** + * string product_id = 1; + * @return The productId. + */ + java.lang.String getProductId(); + /** + * string product_id = 1; + * @return The bytes for productId. + */ + com.google.protobuf.ByteString + getProductIdBytes(); + + /** + * uint32 hcoin = 2; + * @return The hcoin. + */ + int getHcoin(); + + /** + * uint32 remain_days = 3; + * @return The remainDays. + */ + int getRemainDays(); + } + /** + * Protobuf type {@code CardProductRewardNotify} + */ + public static final class CardProductRewardNotify extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:CardProductRewardNotify) + CardProductRewardNotifyOrBuilder { + private static final long serialVersionUID = 0L; + // Use CardProductRewardNotify.newBuilder() to construct. + private CardProductRewardNotify(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private CardProductRewardNotify() { + productId_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new CardProductRewardNotify(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private CardProductRewardNotify( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + productId_ = s; + break; + } + case 16: { + + hcoin_ = input.readUInt32(); + break; + } + case 24: { + + remainDays_ = input.readUInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return CardProductRewardNotifyOuterClass.internal_static_CardProductRewardNotify_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return CardProductRewardNotifyOuterClass.internal_static_CardProductRewardNotify_fieldAccessorTable + .ensureFieldAccessorsInitialized( + CardProductRewardNotifyOuterClass.CardProductRewardNotify.class, CardProductRewardNotifyOuterClass.CardProductRewardNotify.Builder.class); + } + + /** + * Protobuf enum {@code CardProductRewardNotify.CmdId} + */ + public enum CmdId + implements com.google.protobuf.ProtocolMessageEnum { + /** + * NONE = 0; + */ + NONE(0, 0), + /** + * ENET_IS_RELIABLE = 1; + */ + ENET_IS_RELIABLE(2, 1), + /** + * CMD_ID = 4105; + */ + CMD_ID(3, 4105), + UNRECOGNIZED(-1, -1), + ; + + /** + * ENET_CHANNEL_ID = 0; + */ + public static final CmdId ENET_CHANNEL_ID = NONE; + /** + * NONE = 0; + */ + public static final int NONE_VALUE = 0; + /** + * ENET_CHANNEL_ID = 0; + */ + public static final int ENET_CHANNEL_ID_VALUE = 0; + /** + * ENET_IS_RELIABLE = 1; + */ + public static final int ENET_IS_RELIABLE_VALUE = 1; + /** + * CMD_ID = 4105; + */ + public static final int CMD_ID_VALUE = 4105; + + + public final int getNumber() { + if (index == -1) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static CmdId valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static CmdId forNumber(int value) { + switch (value) { + case 0: return NONE; + case 1: return ENET_IS_RELIABLE; + case 4105: return CMD_ID; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + CmdId> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public CmdId findValueByNumber(int number) { + return CmdId.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (index == -1) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return CardProductRewardNotifyOuterClass.CardProductRewardNotify.getDescriptor().getEnumTypes().get(0); + } + + private static final CmdId[] VALUES = getStaticValuesArray(); + private static CmdId[] getStaticValuesArray() { + return new CmdId[] { + NONE, ENET_CHANNEL_ID, ENET_IS_RELIABLE, CMD_ID, + }; + } + public static CmdId valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private CmdId(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:CardProductRewardNotify.CmdId) + } + + public static final int PRODUCT_ID_FIELD_NUMBER = 1; + private volatile java.lang.Object productId_; + /** + * string product_id = 1; + * @return The productId. + */ + @java.lang.Override + public java.lang.String getProductId() { + java.lang.Object ref = productId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + productId_ = s; + return s; + } + } + /** + * string product_id = 1; + * @return The bytes for productId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getProductIdBytes() { + java.lang.Object ref = productId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + productId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HCOIN_FIELD_NUMBER = 2; + private int hcoin_; + /** + * uint32 hcoin = 2; + * @return The hcoin. + */ + @java.lang.Override + public int getHcoin() { + return hcoin_; + } + + public static final int REMAIN_DAYS_FIELD_NUMBER = 3; + private int remainDays_; + /** + * uint32 remain_days = 3; + * @return The remainDays. + */ + @java.lang.Override + public int getRemainDays() { + return remainDays_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(productId_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, productId_); + } + if (hcoin_ != 0) { + output.writeUInt32(2, hcoin_); + } + if (remainDays_ != 0) { + output.writeUInt32(3, remainDays_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(productId_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, productId_); + } + if (hcoin_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, hcoin_); + } + if (remainDays_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, remainDays_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof CardProductRewardNotifyOuterClass.CardProductRewardNotify)) { + return super.equals(obj); + } + CardProductRewardNotifyOuterClass.CardProductRewardNotify other = (CardProductRewardNotifyOuterClass.CardProductRewardNotify) obj; + + if (!getProductId() + .equals(other.getProductId())) return false; + if (getHcoin() + != other.getHcoin()) return false; + if (getRemainDays() + != other.getRemainDays()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + PRODUCT_ID_FIELD_NUMBER; + hash = (53 * hash) + getProductId().hashCode(); + hash = (37 * hash) + HCOIN_FIELD_NUMBER; + hash = (53 * hash) + getHcoin(); + hash = (37 * hash) + REMAIN_DAYS_FIELD_NUMBER; + hash = (53 * hash) + getRemainDays(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(CardProductRewardNotifyOuterClass.CardProductRewardNotify prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code CardProductRewardNotify} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:CardProductRewardNotify) + CardProductRewardNotifyOuterClass.CardProductRewardNotifyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return CardProductRewardNotifyOuterClass.internal_static_CardProductRewardNotify_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return CardProductRewardNotifyOuterClass.internal_static_CardProductRewardNotify_fieldAccessorTable + .ensureFieldAccessorsInitialized( + CardProductRewardNotifyOuterClass.CardProductRewardNotify.class, CardProductRewardNotifyOuterClass.CardProductRewardNotify.Builder.class); + } + + // Construct using CardProductRewardNotifyOuterClass.CardProductRewardNotify.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + productId_ = ""; + + hcoin_ = 0; + + remainDays_ = 0; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return CardProductRewardNotifyOuterClass.internal_static_CardProductRewardNotify_descriptor; + } + + @java.lang.Override + public CardProductRewardNotifyOuterClass.CardProductRewardNotify getDefaultInstanceForType() { + return CardProductRewardNotifyOuterClass.CardProductRewardNotify.getDefaultInstance(); + } + + @java.lang.Override + public CardProductRewardNotifyOuterClass.CardProductRewardNotify build() { + CardProductRewardNotifyOuterClass.CardProductRewardNotify result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public CardProductRewardNotifyOuterClass.CardProductRewardNotify buildPartial() { + CardProductRewardNotifyOuterClass.CardProductRewardNotify result = new CardProductRewardNotifyOuterClass.CardProductRewardNotify(this); + result.productId_ = productId_; + result.hcoin_ = hcoin_; + result.remainDays_ = remainDays_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof CardProductRewardNotifyOuterClass.CardProductRewardNotify) { + return mergeFrom((CardProductRewardNotifyOuterClass.CardProductRewardNotify)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(CardProductRewardNotifyOuterClass.CardProductRewardNotify other) { + if (other == CardProductRewardNotifyOuterClass.CardProductRewardNotify.getDefaultInstance()) return this; + if (!other.getProductId().isEmpty()) { + productId_ = other.productId_; + onChanged(); + } + if (other.getHcoin() != 0) { + setHcoin(other.getHcoin()); + } + if (other.getRemainDays() != 0) { + setRemainDays(other.getRemainDays()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + CardProductRewardNotifyOuterClass.CardProductRewardNotify parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (CardProductRewardNotifyOuterClass.CardProductRewardNotify) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private java.lang.Object productId_ = ""; + /** + * string product_id = 1; + * @return The productId. + */ + public java.lang.String getProductId() { + java.lang.Object ref = productId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + productId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string product_id = 1; + * @return The bytes for productId. + */ + public com.google.protobuf.ByteString + getProductIdBytes() { + java.lang.Object ref = productId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + productId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string product_id = 1; + * @param value The productId to set. + * @return This builder for chaining. + */ + public Builder setProductId( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + productId_ = value; + onChanged(); + return this; + } + /** + * string product_id = 1; + * @return This builder for chaining. + */ + public Builder clearProductId() { + + productId_ = getDefaultInstance().getProductId(); + onChanged(); + return this; + } + /** + * string product_id = 1; + * @param value The bytes for productId to set. + * @return This builder for chaining. + */ + public Builder setProductIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + productId_ = value; + onChanged(); + return this; + } + + private int hcoin_ ; + /** + * uint32 hcoin = 2; + * @return The hcoin. + */ + @java.lang.Override + public int getHcoin() { + return hcoin_; + } + /** + * uint32 hcoin = 2; + * @param value The hcoin to set. + * @return This builder for chaining. + */ + public Builder setHcoin(int value) { + + hcoin_ = value; + onChanged(); + return this; + } + /** + * uint32 hcoin = 2; + * @return This builder for chaining. + */ + public Builder clearHcoin() { + + hcoin_ = 0; + onChanged(); + return this; + } + + private int remainDays_ ; + /** + * uint32 remain_days = 3; + * @return The remainDays. + */ + @java.lang.Override + public int getRemainDays() { + return remainDays_; + } + /** + * uint32 remain_days = 3; + * @param value The remainDays to set. + * @return This builder for chaining. + */ + public Builder setRemainDays(int value) { + + remainDays_ = value; + onChanged(); + return this; + } + /** + * uint32 remain_days = 3; + * @return This builder for chaining. + */ + public Builder clearRemainDays() { + + remainDays_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:CardProductRewardNotify) + } + + // @@protoc_insertion_point(class_scope:CardProductRewardNotify) + private static final CardProductRewardNotifyOuterClass.CardProductRewardNotify DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new CardProductRewardNotifyOuterClass.CardProductRewardNotify(); + } + + public static CardProductRewardNotifyOuterClass.CardProductRewardNotify getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public CardProductRewardNotify parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CardProductRewardNotify(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public CardProductRewardNotifyOuterClass.CardProductRewardNotify getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_CardProductRewardNotify_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_CardProductRewardNotify_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\035CardProductRewardNotify.proto\"\240\001\n\027Card" + + "ProductRewardNotify\022\022\n\nproduct_id\030\001 \001(\t\022" + + "\r\n\005hcoin\030\002 \001(\r\022\023\n\013remain_days\030\003 \001(\r\"M\n\005C" + + "mdId\022\010\n\004NONE\020\000\022\023\n\017ENET_CHANNEL_ID\020\000\022\024\n\020E" + + "NET_IS_RELIABLE\020\001\022\013\n\006CMD_ID\020\211 \032\002\020\001b\006prot" + + "o3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_CardProductRewardNotify_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_CardProductRewardNotify_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_CardProductRewardNotify_descriptor, + new java.lang.String[] { "ProductId", "Hcoin", "RemainDays", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/emu/grasscutter/task/Task.java b/src/main/java/emu/grasscutter/task/Task.java new file mode 100644 index 000000000..88c0ac20c --- /dev/null +++ b/src/main/java/emu/grasscutter/task/Task.java @@ -0,0 +1,11 @@ +package emu.grasscutter.task; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Task { + String taskName() default "NO_NAME"; + String taskCronExpression() default "0 0 0 0 0 ?"; + String triggerName() default "NO_NAME"; +} diff --git a/src/main/java/emu/grasscutter/task/TaskHandler.java b/src/main/java/emu/grasscutter/task/TaskHandler.java new file mode 100644 index 000000000..e1a160a07 --- /dev/null +++ b/src/main/java/emu/grasscutter/task/TaskHandler.java @@ -0,0 +1,11 @@ +package emu.grasscutter.task; + +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +public interface TaskHandler extends Job { + default void execute(JobExecutionContext context) throws JobExecutionException { + + } +} diff --git a/src/main/java/emu/grasscutter/task/TaskMap.java b/src/main/java/emu/grasscutter/task/TaskMap.java new file mode 100644 index 000000000..95be8bb70 --- /dev/null +++ b/src/main/java/emu/grasscutter/task/TaskMap.java @@ -0,0 +1,95 @@ +package emu.grasscutter.task; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.impl.StdSchedulerFactory; +import org.quartz.spi.MutableTrigger; +import org.reflections.Reflections; + +import java.util.*; + +@SuppressWarnings({"UnusedReturnValue", "unused"}) +public final class TaskMap { + private final Map tasks = new HashMap<>(); + private final Map annotations = new HashMap<>(); + private final SchedulerFactory schedulerFactory = new StdSchedulerFactory(); + + public TaskMap() { + this(false); + } + + public TaskMap(boolean scan) { + if (scan) this.scan(); + } + + public static TaskMap getInstance() { + return Grasscutter.getGameServer().getTaskMap(); + } + + public TaskMap registerTask(String taskName, TaskHandler task) { + Task annotation = task.getClass().getAnnotation(Task.class); + this.annotations.put(taskName, annotation); + this.tasks.put(taskName, task); + + // register task + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + JobDetail job = JobBuilder + .newJob(task.getClass()) + .withIdentity(taskName) + .build(); + + Trigger convTrigger = TriggerBuilder.newTrigger() + .withIdentity(annotation.triggerName()) + .withSchedule(CronScheduleBuilder.cronSchedule(annotation.taskCronExpression())) + .build(); + + scheduler.scheduleJob(job, convTrigger); + scheduler.start(); + } catch (SchedulerException e) { + e.printStackTrace(); + } + + return this; + } + + public List getHandlersAsList() { + return new LinkedList<>(this.tasks.values()); + } + + public HashMap getHandlers() { + return new LinkedHashMap<>(this.tasks); + } + + public TaskHandler getHandler(String taskName) { + return this.tasks.get(taskName); + } + + private void scan() { + Reflections reflector = Grasscutter.reflector; + Set> classes = reflector.getTypesAnnotatedWith(Task.class); + System.out.println("Found " + classes.size() + " tasks."); + classes.forEach(annotated -> { + try { + Task taskData = annotated.getAnnotation(Task.class); + Object object = annotated.newInstance(); + if (object instanceof TaskHandler) + this.registerTask(taskData.taskName(), (TaskHandler) object); + else Grasscutter.getLogger().error("Class " + annotated.getName() + " is not a TaskHandler!"); + } catch (Exception exception) { + Grasscutter.getLogger().error("Failed to register task handler for " + annotated.getSimpleName(), exception); + } + }); + } +} diff --git a/src/main/java/emu/grasscutter/task/tasks/MoonCard.java b/src/main/java/emu/grasscutter/task/tasks/MoonCard.java new file mode 100644 index 000000000..71121bcc2 --- /dev/null +++ b/src/main/java/emu/grasscutter/task/tasks/MoonCard.java @@ -0,0 +1,26 @@ +package emu.grasscutter.task.tasks; + +import emu.grasscutter.database.DatabaseManager; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.task.Task; +import emu.grasscutter.task.TaskHandler; + +import java.util.List; + +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +@Task(taskName = "MoonCard", taskCronExpression = "0 0 0 * * ?", triggerName = "MoonCardTrigger") +public final class MoonCard implements TaskHandler { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + List players = DatabaseManager.getDatastore().find(GenshinPlayer.class).stream().toList(); + for (GenshinPlayer player : players) { + if (player.isOnline()) { + if (player.inMoonCard()) { + player.getTodayMoonCard(); + } + } + } + } +}