package tc.oc.commons.bukkit.settings; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import com.google.common.collect.ImmutableMap; import me.anxuiz.settings.Setting; import me.anxuiz.settings.SettingCallback; import me.anxuiz.settings.SettingCallbackManager; import me.anxuiz.settings.SettingManager; import me.anxuiz.settings.SettingRegistry; import me.anxuiz.settings.TypeParseException; import me.anxuiz.settings.base.AbstractSettingManager; import me.anxuiz.settings.bukkit.PlayerSettingCallback; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.bukkit.users.OnlinePlayers; import tc.oc.api.docs.Server; import tc.oc.api.docs.User; import tc.oc.api.users.ChangeSettingRequest; import tc.oc.api.users.UserService; import tc.oc.commons.bukkit.event.UserLoginEvent; import tc.oc.commons.core.plugin.PluginFacet; /** * A singleton with several responsibilities related to player settings: * * - Provides access to settings through the {@link SettingManagerProvider} interface * - Loads saved settings on player login * - Listens for setting changes and saves them * - Registers settings and callbacks that were bound at configuration time */ @Singleton class SettingManagerProviderImpl implements SettingManagerProvider, Listener, PluginFacet { private final Server localServer; private final BukkitUserStore userStore; private final UserService userService; private final OnlinePlayers onlinePlayers; private final SettingRegistry settingRegistry; @Inject SettingManagerProviderImpl(Server localServer, SettingCallbackManager callbackManager, BukkitUserStore userStore, UserService userService, OnlinePlayers onlinePlayers, SettingRegistry settingRegistry, Set<Setting> settings, Map<Setting, SettingCallback> callbacks) { this.localServer = localServer; this.userStore = userStore; this.userService = userService; this.onlinePlayers = onlinePlayers; this.settingRegistry = settingRegistry; callbackManager.addGlobalCallback(new Callback()); settings.forEach(settingRegistry::register); callbacks.forEach(callbackManager::addCallback); } @Override public SettingManager getManager(Player player) { return me.anxuiz.settings.bukkit.PlayerSettings.getManager(player); } @Override public SettingManager getManager(User user) { final Player player = onlinePlayers.find(user); return player != null ? getManager(player) : new UserSettingManager(user); } /** * On login, copy settings from the player's {@link User} document * to their PlayerSettingManager. */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void loadSettings(UserLoginEvent event) { new UserSettingManager(event.getUser()).copyTo(getManager(event.getPlayer())); } /** * Push setting changes to the API */ private class Callback extends PlayerSettingCallback { @Override public void notifyChange(@Nonnull Player player, @Nonnull Setting setting, @Nullable Object oldValue, @Nullable Object newValue) { userService.changeSetting(userStore.getUser(player), new ChangeSettingRequest() { @Override public String profile() { return localServer.settings_profile(); } @Override public String setting() { return setting.getName(); } @Override public String value() { return newValue == null ? null : setting.getType().serialize(newValue); } }); } } /** * Read settings directly from a {@link User} document. * * This is read-only, write methods will throw an exception. */ private class UserSettingManager extends AbstractSettingManager { final User user; UserSettingManager(User user) { this.user = user; } Map<String, String> profile() { return Optional.ofNullable(user.mc_settings_by_profile() .get(localServer.settings_profile())) .orElseGet(ImmutableMap::of); } void copyTo(SettingManager that) { final Map<String, String> profile = profile(); settingRegistry.getSettings().forEach(setting -> { that.setValue(setting, getRawValue(setting, profile), false); }); } @Nullable Object getRawValue(Setting setting, Map<String, String> profile) { final String text = profile.get(setting.getName()); if(text == null) return null; try { return setting.getType().parse(text); } catch(TypeParseException e) { return null; } } @Override public @Nullable Object getRawValue(Setting setting) { return getRawValue(setting, profile()); } @Override protected void setRawValue(Setting setting, Object value) { throw new UnsupportedOperationException(); } @Override public SettingCallbackManager getCallbackManager() { throw new UnsupportedOperationException(); } } }