mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-25 12:52:57 +08:00
implement the activity system
This commit is contained in:
parent
5d35cb49b4
commit
977f1ca2ea
@ -15,6 +15,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public class GameData {
|
public class GameData {
|
||||||
// BinOutputs
|
// BinOutputs
|
||||||
@ -98,6 +99,9 @@ public class GameData {
|
|||||||
private static final Int2ObjectMap<BattlePassMissionData> battlePassMissionDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<BattlePassMissionData> battlePassMissionDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<BattlePassRewardData> battlePassRewardDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<BattlePassRewardData> battlePassRewardDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
@Getter private static final Int2ObjectMap<ActivityData> activityDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
@Getter private static final Int2ObjectMap<ActivityWatcherData> activityWatcherDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||||
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
|
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
|
||||||
|
34
src/main/java/emu/grasscutter/data/excels/ActivityData.java
Normal file
34
src/main/java/emu/grasscutter/data/excels/ActivityData.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ResourceType(name = "NewActivityExcelConfigData.json", loadPriority = ResourceType.LoadPriority.LOW)
|
||||||
|
@Getter
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class ActivityData extends GameResource {
|
||||||
|
int activityId;
|
||||||
|
String activityType;
|
||||||
|
List<Integer> condGroupId;
|
||||||
|
List<Integer> watcherId;
|
||||||
|
List<ActivityWatcherData> watcherDataList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.activityId;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
this.watcherDataList = watcherId.stream().map(item -> GameData.getActivityWatcherDataMap().get(item.intValue()))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ResourceType(name = "NewActivityWatcherConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
|
||||||
|
@Getter
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class ActivityWatcherData extends GameResource {
|
||||||
|
int id;
|
||||||
|
int rewardID;
|
||||||
|
int progress;
|
||||||
|
WatcherTrigger triggerConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
triggerConfig.paramList = triggerConfig.paramList.stream().filter(x -> !x.isBlank()).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public static class WatcherTrigger{
|
||||||
|
String triggerType;
|
||||||
|
List<String> paramList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,6 +10,7 @@ import dev.morphia.query.experimental.filters.Filters;
|
|||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||||
import emu.grasscutter.game.friends.Friendship;
|
import emu.grasscutter.game.friends.Friendship;
|
||||||
@ -326,4 +327,14 @@ public final class DatabaseHelper {
|
|||||||
public static void saveBattlePass(BattlePassManager manager) {
|
public static void saveBattlePass(BattlePassManager manager) {
|
||||||
DatabaseManager.getGameDatastore().save(manager);
|
DatabaseManager.getGameDatastore().save(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PlayerActivityData getPlayerActivityData(int uid, int activityId) {
|
||||||
|
return DatabaseManager.getGameDatastore().find(PlayerActivityData.class)
|
||||||
|
.filter(Filters.and(Filters.eq("uid", uid),Filters.eq("activityId", activityId)))
|
||||||
|
.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void savePlayerActivityData(PlayerActivityData playerActivityData) {
|
||||||
|
DatabaseManager.getGameDatastore().save(playerActivityData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import dev.morphia.query.experimental.filters.Filters;
|
|||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.Grasscutter.ServerRunMode;
|
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||||
import emu.grasscutter.game.friends.Friendship;
|
import emu.grasscutter.game.friends.Friendship;
|
||||||
@ -29,12 +30,11 @@ import static emu.grasscutter.Configuration.*;
|
|||||||
public final class DatabaseManager {
|
public final class DatabaseManager {
|
||||||
private static Datastore gameDatastore;
|
private static Datastore gameDatastore;
|
||||||
private static Datastore dispatchDatastore;
|
private static Datastore dispatchDatastore;
|
||||||
|
|
||||||
private static final Class<?>[] mappedClasses = new Class<?>[] {
|
private static final Class<?>[] mappedClasses = new Class<?>[] {
|
||||||
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
|
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
|
||||||
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class
|
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class, PlayerActivityData.class
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Datastore getGameDatastore() {
|
public static Datastore getGameDatastore() {
|
||||||
return gameDatastore;
|
return gameDatastore;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class ActivityConfigItem {
|
||||||
|
int activityId;
|
||||||
|
int activityType;
|
||||||
|
int scheduleId;
|
||||||
|
List<Integer> meetCondList;
|
||||||
|
Date beginTime;
|
||||||
|
Date endTime;
|
||||||
|
|
||||||
|
transient ActivityHandler activityHandler;
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.excels.ActivityData;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||||
|
import emu.grasscutter.utils.DateHelper;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public abstract class ActivityHandler {
|
||||||
|
/**
|
||||||
|
* Must set before initWatchers
|
||||||
|
*/
|
||||||
|
ActivityConfigItem activityConfigItem;
|
||||||
|
ActivityData activityData;
|
||||||
|
Map<WatcherTriggerType, List<ActivityWatcher>> watchersMap = new HashMap<>();
|
||||||
|
|
||||||
|
public void initWatchers(HashMap<String, ConstructorAccess<?>> activityWatcherTypeMap){
|
||||||
|
activityData = GameData.getActivityDataMap().get(activityConfigItem.getActivityId());
|
||||||
|
|
||||||
|
// add watcher to map by id
|
||||||
|
activityData.getWatcherDataList().forEach(watcherData -> {
|
||||||
|
var watcherType = activityWatcherTypeMap.get(watcherData.getTriggerConfig().getTriggerType());
|
||||||
|
ActivityWatcher watcher;
|
||||||
|
if(watcherType != null){
|
||||||
|
watcher = (ActivityWatcher) watcherType.newInstance();
|
||||||
|
}else{
|
||||||
|
watcher = new DefaultWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher.setWatcherId(watcherData.getId());
|
||||||
|
watcher.setActivityHandler(this);
|
||||||
|
watcher.setActivityWatcherData(watcherData);
|
||||||
|
watchersMap.computeIfAbsent(WatcherTriggerType.getTypeByName(watcherData.getTriggerConfig().getTriggerType()), k -> new ArrayList<>());
|
||||||
|
watchersMap.get(WatcherTriggerType.getTypeByName(watcherData.getTriggerConfig().getTriggerType())).add(watcher);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, PlayerActivityData.WatcherInfo> initWatchersDataForPlayer(){
|
||||||
|
return watchersMap.values().stream()
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(PlayerActivityData.WatcherInfo::init)
|
||||||
|
.collect(Collectors.toMap(PlayerActivityData.WatcherInfo::getWatcherId, y -> y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerActivityData initPlayerActivityData(Player player){
|
||||||
|
return PlayerActivityData.of()
|
||||||
|
.activityId(activityConfigItem.getActivityId())
|
||||||
|
.uid(player.getUid())
|
||||||
|
.watcherInfoMap(initWatchersDataForPlayer())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void buildProto(PlayerActivityData playerActivityData, ActivityInfoOuterClass.ActivityInfo.Builder activityInfo){
|
||||||
|
activityInfo.setActivityId(activityConfigItem.getActivityId())
|
||||||
|
.setActivityType(activityConfigItem.getActivityType())
|
||||||
|
.setScheduleId(activityConfigItem.getScheduleId())
|
||||||
|
.setBeginTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
|
||||||
|
.setFirstDayStartTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
|
||||||
|
.setEndTime(DateHelper.getUnixTime(activityConfigItem.getEndTime()))
|
||||||
|
.addAllMeetCondList(activityConfigItem.getMeetCondList());
|
||||||
|
|
||||||
|
if (playerActivityData != null){
|
||||||
|
activityInfo.addAllWatcherInfoList(playerActivityData.getAllWatcherInfoList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
src/main/java/emu/grasscutter/game/activity/ActivityManager.java
Normal file
125
src/main/java/emu/grasscutter/game/activity/ActivityManager.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.DataLoader;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ActivityManager {
|
||||||
|
private static final Map<Integer, ActivityConfigItem> activityConfigItemMap;
|
||||||
|
private final Player player;
|
||||||
|
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
activityConfigItemMap = new HashMap<>();
|
||||||
|
|
||||||
|
loadActivityConfigData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActivityManager(Player player){
|
||||||
|
this.player = player;
|
||||||
|
|
||||||
|
playerActivityDataMap = new ConcurrentHashMap<>();
|
||||||
|
// load data for player
|
||||||
|
activityConfigItemMap.values().forEach(item -> {
|
||||||
|
var data = PlayerActivityData.getByPlayer(player, item.getActivityId());
|
||||||
|
if(data == null){
|
||||||
|
data = item.getActivityHandler().initPlayerActivityData(player);
|
||||||
|
data.save();
|
||||||
|
}
|
||||||
|
data.setPlayer(player);
|
||||||
|
playerActivityDataMap.put(item.getActivityId(), data);
|
||||||
|
});
|
||||||
|
|
||||||
|
player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadActivityConfigData() {
|
||||||
|
// scan activity type handler & watcher type
|
||||||
|
var activityHandlerTypeMap = new HashMap<String, ConstructorAccess<?>>();
|
||||||
|
var activityWatcherTypeMap = new HashMap<String, ConstructorAccess<?>>();
|
||||||
|
var reflections = new Reflections(ActivityManager.class.getPackage().getName());
|
||||||
|
|
||||||
|
reflections.getSubTypesOf(ActivityHandler.class).forEach(item -> {
|
||||||
|
var typeName = item.getAnnotation(ActivityType.class);
|
||||||
|
activityHandlerTypeMap.put(typeName.value(), ConstructorAccess.get(item));
|
||||||
|
});
|
||||||
|
reflections.getSubTypesOf(ActivityWatcher.class).forEach(item -> {
|
||||||
|
var typeName = item.getAnnotation(WatcherType.class);
|
||||||
|
activityWatcherTypeMap.put(typeName.value().name(), ConstructorAccess.get(item));
|
||||||
|
});
|
||||||
|
|
||||||
|
try(InputStream is = DataLoader.load("ActivityConfig.json"); InputStreamReader isr = new InputStreamReader(is)) {
|
||||||
|
List<ActivityConfigItem> activities = Grasscutter.getGsonFactory().fromJson(
|
||||||
|
isr,
|
||||||
|
TypeToken.getParameterized(List.class, ActivityConfigItem.class).getType());
|
||||||
|
|
||||||
|
|
||||||
|
activities.forEach(item -> {
|
||||||
|
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
|
||||||
|
if(activityData == null){
|
||||||
|
Grasscutter.getLogger().warn("activity {} not exist.", item.getActivityId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var activityHandlerType = activityHandlerTypeMap.get(activityData.getActivityType());
|
||||||
|
|
||||||
|
if(activityHandlerType != null) {
|
||||||
|
var activityHandler = (ActivityHandler) activityHandlerType.newInstance();
|
||||||
|
activityHandler.setActivityConfigItem(item);
|
||||||
|
activityHandler.initWatchers(activityWatcherTypeMap);
|
||||||
|
item.setActivityHandler(activityHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
activityConfigItemMap.putIfAbsent(item.getActivityId(), item);
|
||||||
|
});
|
||||||
|
|
||||||
|
Grasscutter.getLogger().error("Enable {} activities.", activityConfigItemMap.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActivityInfoOuterClass.ActivityInfo getInfoProto(int activityId){
|
||||||
|
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
|
||||||
|
var activityData = playerActivityDataMap.get(activityId);
|
||||||
|
|
||||||
|
var proto = ActivityInfoOuterClass.ActivityInfo.newBuilder();
|
||||||
|
activityHandler.buildProto(activityData, proto);
|
||||||
|
|
||||||
|
return proto.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* trigger activity watcher
|
||||||
|
* @param watcherTriggerType
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
public void triggerWatcher(WatcherTriggerType watcherTriggerType, String... params) {
|
||||||
|
var watchers = activityConfigItemMap.values().stream()
|
||||||
|
.map(ActivityConfigItem::getActivityHandler)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(ActivityHandler::getWatchersMap)
|
||||||
|
.map(map -> map.get(watcherTriggerType))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
watchers.forEach(watcher -> watcher.trigger(
|
||||||
|
playerActivityDataMap.get(watcher.getActivityHandler().getActivityConfigItem().getActivityId()),
|
||||||
|
params));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface ActivityType {
|
||||||
|
String value();
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.ActivityWatcherData;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public abstract class ActivityWatcher {
|
||||||
|
int watcherId;
|
||||||
|
ActivityWatcherData activityWatcherData;
|
||||||
|
ActivityHandler activityHandler;
|
||||||
|
|
||||||
|
protected abstract boolean isMeet(String... param);
|
||||||
|
|
||||||
|
public void trigger(PlayerActivityData playerActivityData, String... param){
|
||||||
|
if(isMeet(param)){
|
||||||
|
playerActivityData.addWatcherProgress(watcherId);
|
||||||
|
playerActivityData.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
|
||||||
|
@WatcherType(WatcherTriggerType.TRIGGER_NONE)
|
||||||
|
public class DefaultWatcher extends ActivityWatcher{
|
||||||
|
@Override
|
||||||
|
protected boolean isMeet(String... param) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import dev.morphia.annotations.Entity;
|
||||||
|
import dev.morphia.annotations.Id;
|
||||||
|
import dev.morphia.annotations.Transient;
|
||||||
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.ActivityWatcherInfoOuterClass;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Entity("activities")
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
@Builder(builderMethodName = "of")
|
||||||
|
public class PlayerActivityData {
|
||||||
|
@Id
|
||||||
|
String id;
|
||||||
|
int uid;
|
||||||
|
int activityId;
|
||||||
|
Map<Integer, WatcherInfo> watcherInfoMap;
|
||||||
|
String detail;
|
||||||
|
@Transient Player player;
|
||||||
|
|
||||||
|
public void save(){
|
||||||
|
DatabaseHelper.savePlayerActivityData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlayerActivityData getByPlayer(Player player, int activityId){
|
||||||
|
return DatabaseHelper.getPlayerActivityData(player.getUid(), activityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addWatcherProgress(int watcherId){
|
||||||
|
var watcherInfo = watcherInfoMap.get(watcherId);
|
||||||
|
if(watcherInfo == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(watcherInfo.curProgress >= watcherInfo.totalProgress){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
watcherInfo.curProgress++;
|
||||||
|
getPlayer().sendPacket(new PacketActivityUpdateWatcherNotify(activityId, watcherInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ActivityWatcherInfoOuterClass.ActivityWatcherInfo> getAllWatcherInfoList() {
|
||||||
|
return watcherInfoMap.values().stream()
|
||||||
|
.map(WatcherInfo::toProto)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
@Builder(builderMethodName = "of")
|
||||||
|
public static class WatcherInfo{
|
||||||
|
int watcherId;
|
||||||
|
int totalProgress;
|
||||||
|
int curProgress;
|
||||||
|
boolean isTakenReward;
|
||||||
|
|
||||||
|
public static WatcherInfo init(ActivityWatcher watcher){
|
||||||
|
return WatcherInfo.of()
|
||||||
|
.watcherId(watcher.getWatcherId())
|
||||||
|
.totalProgress(watcher.getActivityWatcherData().getProgress())
|
||||||
|
.isTakenReward(false)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActivityWatcherInfoOuterClass.ActivityWatcherInfo toProto(){
|
||||||
|
return ActivityWatcherInfoOuterClass.ActivityWatcherInfo.newBuilder()
|
||||||
|
.setWatcherId(watcherId)
|
||||||
|
.setCurProgress(curProgress)
|
||||||
|
.setTotalProgress(totalProgress)
|
||||||
|
.setIsTakenReward(isTakenReward)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/emu/grasscutter/game/activity/WatcherType.java
Normal file
14
src/main/java/emu/grasscutter/game/activity/WatcherType.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface WatcherType {
|
||||||
|
WatcherTriggerType value();
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.activity.musicgame;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityHandler;
|
||||||
|
import emu.grasscutter.game.activity.ActivityType;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||||
|
|
||||||
|
@ActivityType("NEW_ACTIVITY_MUSIC_GAME")
|
||||||
|
public class MusicGameActivityHandler extends ActivityHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buildProto(PlayerActivityData playerActivityData, ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
|
||||||
|
super.buildProto(playerActivityData, activityInfo);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.activity.musicgame;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||||
|
import emu.grasscutter.game.activity.WatcherType;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
|
||||||
|
@WatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
|
||||||
|
public class MusicGameScoreTrigger extends ActivityWatcher {
|
||||||
|
@Override
|
||||||
|
protected boolean isMeet(String... param) {
|
||||||
|
if(param.length != 2){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var paramList = getActivityWatcherData().getTriggerConfig().getParamList();
|
||||||
|
if(!paramList.get(0).equals(param[0])){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var score = Integer.parseInt(param[1]);
|
||||||
|
var target = Integer.parseInt(paramList.get(1));
|
||||||
|
return score >= target;
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import emu.grasscutter.database.DatabaseManager;
|
|||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.game.CoopRequest;
|
import emu.grasscutter.game.CoopRequest;
|
||||||
import emu.grasscutter.game.ability.AbilityManager;
|
import emu.grasscutter.game.ability.AbilityManager;
|
||||||
|
import emu.grasscutter.game.activity.ActivityManager;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.avatar.AvatarProfileData;
|
import emu.grasscutter.game.avatar.AvatarProfileData;
|
||||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||||
@ -183,6 +184,7 @@ public class Player {
|
|||||||
@Transient private GameHome home;
|
@Transient private GameHome home;
|
||||||
@Transient private FurnitureManager furnitureManager;
|
@Transient private FurnitureManager furnitureManager;
|
||||||
@Transient private BattlePassManager battlePassManager;
|
@Transient private BattlePassManager battlePassManager;
|
||||||
|
@Getter @Transient private ActivityManager activityManager;
|
||||||
|
|
||||||
@Transient private CollectionManager collectionManager;
|
@Transient private CollectionManager collectionManager;
|
||||||
private CollectionRecordStore collectionRecordStore;
|
private CollectionRecordStore collectionRecordStore;
|
||||||
@ -1508,11 +1510,13 @@ public class Player {
|
|||||||
|
|
||||||
// Battle Pass trigger
|
// Battle Pass trigger
|
||||||
this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN);
|
this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN);
|
||||||
|
|
||||||
this.furnitureManager.onLogin();
|
this.furnitureManager.onLogin();
|
||||||
// Home
|
// Home
|
||||||
home = GameHome.getByUid(getUid());
|
home = GameHome.getByUid(getUid());
|
||||||
home.onOwnerLogin(this);
|
home.onOwnerLogin(this);
|
||||||
|
// Activity
|
||||||
|
activityManager = new ActivityManager(this);
|
||||||
|
|
||||||
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
|
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
|
||||||
session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
|
session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
|
||||||
|
@ -3,13 +3,20 @@ package emu.grasscutter.server.packet.recv;
|
|||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.proto.GetActivityInfoReqOuterClass;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.server.packet.send.PacketGetActivityInfoRsp;
|
import emu.grasscutter.server.packet.send.PacketGetActivityInfoRsp;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.GetActivityInfoReq)
|
@Opcodes(PacketOpcodes.GetActivityInfoReq)
|
||||||
public class HandlerGetActivityInfoReq extends PacketHandler {
|
public class HandlerGetActivityInfoReq extends PacketHandler {
|
||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
session.send(new PacketGetActivityInfoRsp());
|
var req = GetActivityInfoReqOuterClass.GetActivityInfoReq.parseFrom(payload);
|
||||||
|
|
||||||
|
session.send(new PacketGetActivityInfoRsp(
|
||||||
|
new HashSet<>(req.getActivityIdListList()),
|
||||||
|
session.getPlayer().getActivityManager()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.MusicGameSettleReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMusicGameSettleRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.MusicGameSettleReq)
|
||||||
|
public class HandlerMusicGameSettleReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
var req = MusicGameSettleReqOuterClass.MusicGameSettleReq.parseFrom(payload);
|
||||||
|
|
||||||
|
session.getPlayer().getActivityManager().triggerWatcher(
|
||||||
|
WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE,
|
||||||
|
String.valueOf(req.getMusicBasicId()),
|
||||||
|
String.valueOf(req.getScore())
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//session.send(new PacketMusicGameSettleRsp(req.getMusicBasicId()));
|
||||||
|
session.send(new PacketMusicGameSettleRsp(req.getMusicBasicId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
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.MusicGameStartReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMusicGameStartRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.MusicGameStartReq)
|
||||||
|
public class HandlerMusicGameStartReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
var req = MusicGameStartReqOuterClass.MusicGameStartReq.parseFrom(payload);
|
||||||
|
|
||||||
|
session.send(new PacketMusicGameStartRsp(req.getMusicBasicId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoNotifyOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||||
|
|
||||||
|
public class PacketActivityInfoNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketActivityInfoNotify(ActivityInfoOuterClass.ActivityInfo activityInfo) {
|
||||||
|
super(PacketOpcodes.ActivityInfoNotify);
|
||||||
|
|
||||||
|
var proto = ActivityInfoNotifyOuterClass.ActivityInfoNotify.newBuilder();
|
||||||
|
|
||||||
|
proto.setActivityInfo(activityInfo);
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ActivityScheduleInfoNotifyOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.ActivityScheduleInfoOuterClass;
|
||||||
|
import emu.grasscutter.utils.DateHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class PacketActivityScheduleInfoNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketActivityScheduleInfoNotify(Collection<ActivityConfigItem> activityConfigItemList) {
|
||||||
|
super(PacketOpcodes.ActivityScheduleInfoNotify);
|
||||||
|
|
||||||
|
var proto = ActivityScheduleInfoNotifyOuterClass.ActivityScheduleInfoNotify.newBuilder();
|
||||||
|
|
||||||
|
activityConfigItemList.forEach(item -> {
|
||||||
|
proto.addActivityScheduleList(ActivityScheduleInfoOuterClass.ActivityScheduleInfo.newBuilder()
|
||||||
|
.setActivityId(item.getActivityId())
|
||||||
|
.setScheduleId(item.getScheduleId())
|
||||||
|
.setIsOpen(true)
|
||||||
|
.setBeginTime(DateHelper.getUnixTime(item.getBeginTime()))
|
||||||
|
.setEndTime(DateHelper.getUnixTime(item.getEndTime()))
|
||||||
|
.build());
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ActivityUpdateWatcherNotifyOuterClass;
|
||||||
|
|
||||||
|
public class PacketActivityUpdateWatcherNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketActivityUpdateWatcherNotify(int activityId, PlayerActivityData.WatcherInfo watcherInfo) {
|
||||||
|
super(PacketOpcodes.ActivityUpdateWatcherNotify);
|
||||||
|
|
||||||
|
var proto = ActivityUpdateWatcherNotifyOuterClass.ActivityUpdateWatcherNotify.newBuilder();
|
||||||
|
|
||||||
|
proto.setActivityId(activityId)
|
||||||
|
.setWatcherInfo(watcherInfo.toProto());
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,22 @@
|
|||||||
package emu.grasscutter.server.packet.send;
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityManager;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.GetActivityInfoRspOuterClass.GetActivityInfoRsp;
|
import emu.grasscutter.net.proto.GetActivityInfoRspOuterClass.GetActivityInfoRsp;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class PacketGetActivityInfoRsp extends BasePacket {
|
public class PacketGetActivityInfoRsp extends BasePacket {
|
||||||
public PacketGetActivityInfoRsp() {
|
public PacketGetActivityInfoRsp(Set<Integer> activityIdList, ActivityManager activityManager) {
|
||||||
super(PacketOpcodes.GetActivityInfoRsp);
|
super(PacketOpcodes.GetActivityInfoRsp);
|
||||||
|
|
||||||
GetActivityInfoRsp proto = GetActivityInfoRsp.newBuilder().build();
|
var proto = GetActivityInfoRsp.newBuilder();
|
||||||
|
|
||||||
|
activityIdList.stream()
|
||||||
|
.map(activityManager::getInfoProto)
|
||||||
|
.forEach(proto::addActivityInfoList);
|
||||||
|
|
||||||
this.setData(proto);
|
this.setData(proto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.MusicGameSettleRspOuterClass;
|
||||||
|
|
||||||
|
public class PacketMusicGameSettleRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketMusicGameSettleRsp(int musicBasicId) {
|
||||||
|
super(PacketOpcodes.MusicGameSettleRsp);
|
||||||
|
|
||||||
|
var proto = MusicGameSettleRspOuterClass.MusicGameSettleRsp.newBuilder();
|
||||||
|
|
||||||
|
proto.setMusicBasicId(musicBasicId)
|
||||||
|
.setIsNewRecord(true);
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.MusicGameStartRspOuterClass;
|
||||||
|
|
||||||
|
public class PacketMusicGameStartRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketMusicGameStartRsp(int musicBasicId) {
|
||||||
|
super(PacketOpcodes.MusicGameStartRsp);
|
||||||
|
|
||||||
|
var proto = MusicGameStartRspOuterClass.MusicGameStartRsp.newBuilder();
|
||||||
|
|
||||||
|
proto.setMusicBasicId(musicBasicId);
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
17
src/main/resources/defaults/data/ActivityConfig.json
Normal file
17
src/main/resources/defaults/data/ActivityConfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"activityId" : 5072,
|
||||||
|
"activityType" : 2202,
|
||||||
|
"meetCondList" : [
|
||||||
|
5072001,
|
||||||
|
5072002,
|
||||||
|
5072003,
|
||||||
|
5072004,
|
||||||
|
5072005,
|
||||||
|
5072006,
|
||||||
|
5072007
|
||||||
|
],
|
||||||
|
"beginTime" : "2022-05-01T00:00:00+08:00",
|
||||||
|
"endTime" : "2023-05-01T00:00:00+08:00"
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user