package emu.grasscutter.game.activity; import com.esotericsoftware.reflectasm.ConstructorAccess; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; import emu.grasscutter.game.player.BasePlayerManager; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActivityType; 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.util.*; import java.util.concurrent.ConcurrentHashMap; @Getter public class ActivityManager extends BasePlayerManager { private static final Map activityConfigItemMap; private final Map playerActivityDataMap; static { activityConfigItemMap = new HashMap<>(); loadActivityConfigData(); } private static void loadActivityConfigData() { // scan activity type handler & watcher type var activityHandlerTypeMap = new HashMap>(); var activityWatcherTypeMap = new HashMap>(); var reflections = new Reflections(ActivityManager.class.getPackage().getName()); reflections.getSubTypesOf(ActivityHandler.class).forEach(item -> { var typeName = item.getAnnotation(GameActivity.class); activityHandlerTypeMap.put(typeName.value(), ConstructorAccess.get(item)); }); reflections.getSubTypesOf(ActivityWatcher.class).forEach(item -> { var typeName = item.getAnnotation(ActivityWatcherType.class); activityWatcherTypeMap.put(typeName.value(), ConstructorAccess.get(item)); }); try { DataLoader.loadList("ActivityConfig.json", ActivityConfigItem.class).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(ActivityType.getTypeByName(activityData.getActivityType())); ActivityHandler activityHandler; if (activityHandlerType != null) { activityHandler = (ActivityHandler) activityHandlerType.newInstance(); }else { activityHandler = new DefaultActivityHandler(); } activityHandler.setActivityConfigItem(item); activityHandler.initWatchers(activityWatcherTypeMap); item.setActivityHandler(activityHandler); activityConfigItemMap.putIfAbsent(item.getActivityId(), item); }); Grasscutter.getLogger().info("Enable {} activities.", activityConfigItemMap.size()); } catch (Exception e) { Grasscutter.getLogger().error("Unable to load activities config.", e); } } public ActivityManager(Player player) { super(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); data.setActivityHandler(item.getActivityHandler()); playerActivityDataMap.put(item.getActivityId(), data); }); player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values())); } /** * trigger activity watcher */ 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)); } public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) { var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler(); var activityData = playerActivityDataMap.get(activityId); return activityHandler.toProto(activityData); } public Optional getActivityHandler(ActivityType type) { return activityConfigItemMap.values().stream() .map(ActivityConfigItem::getActivityHandler) .filter(x -> type == x.getClass().getAnnotation(GameActivity.class).value()) .findFirst(); } public Optional getActivityHandlerAs(ActivityType type, Class clazz) { return getActivityHandler(type).map(x -> (T)x); } public Optional getActivityIdByActivityType(ActivityType type) { return getActivityHandler(type) .map(ActivityHandler::getActivityConfigItem) .map(ActivityConfigItem::getActivityId); } public Optional getPlayerActivityDataByActivityType(ActivityType type) { return getActivityIdByActivityType(type) .map(playerActivityDataMap::get); } public Optional getInfoProtoByActivityType(ActivityType type) { return getActivityIdByActivityType(type) .map(this::getInfoProtoByActivityId); } }