package com.mumfrey.webprefs; import java.io.File; import java.net.Proxy; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.mojang.authlib.GameProfile; import com.mojang.realmsclient.dto.RealmsServer; import com.mumfrey.liteloader.JoinGameListener; import com.mumfrey.liteloader.Tickable; import com.mumfrey.liteloader.core.LiteLoader; import com.mumfrey.webprefs.exceptions.InvalidServiceException; import com.mumfrey.webprefs.exceptions.InvalidUUIDException; import com.mumfrey.webprefs.framework.WebPreferencesProvider; import com.mumfrey.webprefs.interfaces.IWebPreferences; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.network.INetHandler; import net.minecraft.network.play.server.SPacketJoinGame; import net.minecraft.util.Session; /** * WebPreferences Service Manager, acts as a central registry for all web * preference collections. * * <p>To access preferences on a service, first request the service using * {@link #get(String)} or {@link #getDefault()}, you can then request * preferences objects from the service by calling the various overloads of * {@link #getPreferences}.</p> * * @author Adam Mummery-Smith */ public final class WebPreferencesManager { /** * WebPreferences Manager Update Daemon is injected into LiteLoader to * facilitate passing events to the WebPreferences Manager without having to * expose public callback methods. * * @author Adam Mummery-Smith */ static class WebPreferencesUpdateDeamon implements Tickable, JoinGameListener { @Override public String getName() { return "Web Preferences Update Daemon"; } @Override public String getVersion() { return "N/A"; } @Override public void init(File configPath) { } @Override public void upgradeSettings(String version, File configPath, File oldConfigPath) { } @Override public void onTick(Minecraft minecraft, float partialTicks, boolean inGame, boolean clock) { if (clock) { for (WebPreferencesManager manager : WebPreferencesManager.managers.values()) { manager.onTick(); } } } @Override public void onJoinGame(INetHandler netHandler, SPacketJoinGame joinGamePacket, ServerData serverData, RealmsServer realmsServer) { for (WebPreferencesManager manager : WebPreferencesManager.managers.values()) { manager.onJoinGame(); } } } /** * Default KV api hostname to connect to */ private static final String DEFAULT_HOSTNAME = "kv.liteloader.com"; /** * Regex for validating UUIDs */ private static final Pattern uuidPattern = Pattern.compile("^[a-f0-9]{32}$"); /** * Mapping of hostnames to managers */ static final Map<String, WebPreferencesManager> managers = new LinkedHashMap<String, WebPreferencesManager>(); /** * Update daemon */ private static WebPreferencesUpdateDeamon updateDeamon; /** * Session for this instance */ private final Session session; /** * Preference provider, manages queueing requests and passing responses back * to clients */ private final WebPreferencesProvider provider; /** * All preference sets, for iteration purposes */ private final List<AbstractWebPreferences> allPreferences = new LinkedList<AbstractWebPreferences>(); /** * All public preference sets, mapped by UUID */ private final Map<String, IWebPreferences> preferencesPublic = new HashMap<String, IWebPreferences>(); /** * All private preference sets, mapped by UUID */ private final Map<String, IWebPreferences> preferencesPrivate = new HashMap<String, IWebPreferences>(); private WebPreferencesManager(Proxy proxy, Session session, String hostName) { this.session = session; this.provider = new WebPreferencesProvider(proxy, session, hostName, 50); } void onTick() { this.provider.onTick(); for (AbstractWebPreferences prefs : this.allPreferences) { try { prefs.onTick(); } catch (Exception ex) {} } } void onJoinGame() { for (AbstractWebPreferences prefs : this.allPreferences) { try { prefs.poll(); } catch (Exception ex) {} } } /** * Get a public or private preferences collection for the local player. If * the game is running in offline mode, a local preference collection is * returned instead. * * @param privatePrefs true to fetch the player's private preferences, false * to fetch public preferences * @return player's preference collection, creates if necessary */ public IWebPreferences getLocalPreferences(boolean privatePrefs) { try { return this.getPreferences(this.session.getPlayerID(), privatePrefs); } catch (InvalidUUIDException ex) { UUID offlineUUID = EntityPlayer.getOfflineUUID(this.session.getUsername()); return this.getOfflinePreferences(offlineUUID, privatePrefs, false, false); } } /** * Get a public preferences collection for the specified player. If the game * is running in offline mode, a dummy preference collection supporting no * operations is returned instead. * * @param player Player to fetch preferences for * @return Preference collection or <tt>null</tt> if the player's profile * cannot be retrieved */ public IWebPreferences getPreferences(EntityPlayer player) { try { return this.getPreferences(player, false); } catch (InvalidUUIDException ex) { String playerName = player.getName(); UUID offlineUUID = EntityPlayer.getOfflineUUID(playerName); return this.getOfflinePreferences(offlineUUID, false, false, !playerName.equals(this.session.getUsername())); } } /** * Get a public or private preferences collection for the specified player, * note that accessing a private collection for another player is likely * to be prohibited by the service. * * @param player Player to fetch preferences for * @param privatePrefs True to fetch the player's private preferences, false * to fetch the public preferences * @return Preference collection or <tt>null</tt> if the player's profile * cannot be retrieved */ public IWebPreferences getPreferences(EntityPlayer player, boolean privatePrefs) { GameProfile gameProfile = player.getGameProfile(); return gameProfile != null ? this.getPreferences(gameProfile, privatePrefs) : null; } /** * Get a public preferences collection for the specified game profile. * * @param gameProfile game profile to fetch preferences for * @return Preference collection or <tt>null</tt> if the supplied profile is * null */ public IWebPreferences getPreferences(GameProfile gameProfile) { return gameProfile != null ? this.getPreferences(gameProfile, false) : null; } /** * Get a public or private preferences collection for the specified game * profile, note that accessing a private collection for another player is * likely to be prohibited by the service. * * @param gameProfile game profile to fetch preferences for * @param privatePrefs True to fetch the player's private preferences, false * to fetch the public preferences * @return Preference collection or <tt>null</tt> if the supplied profile is * null */ public IWebPreferences getPreferences(GameProfile gameProfile, boolean privatePrefs) { return gameProfile != null ? this.getPreferences(gameProfile.getId(), privatePrefs) : null; } /** * Get a public preferences collection for the specified player UUID. * * @param uuid UUID to fetch preferences for * @return Preference collection or <tt>null</tt> if the supplied UUID is * null */ public IWebPreferences getPreferences(UUID uuid) { return uuid != null ? this.getPreferences(uuid, false) : null; } /** * Get a public or private preferences collection for the specified player * UUID, note that accessing a private collection for another player is * likely to be prohibited by the service. * * @param uuid UUID to fetch preferences for * @param privatePrefs True to fetch the player's private preferences, false * to fetch the public preferences * @return Preference collection or <tt>null</tt> if the supplied UUID is * null */ public IWebPreferences getPreferences(UUID uuid, boolean privatePrefs) { return uuid != null ? this.getPreferences(uuid.toString(), privatePrefs) : null; } public IWebPreferences getPreferences(String uuid, boolean privatePrefs) { uuid = this.sanitiseUUID(uuid); Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic; IWebPreferences prefs = preferences.get(uuid); if (prefs == null) { WebPreferences newPrefs = new WebPreferences(this.provider, uuid, privatePrefs, !uuid.equals(this.session.getPlayerID())); this.allPreferences.add(newPrefs); preferences.put(uuid, newPrefs); prefs = newPrefs; } return prefs; } private IWebPreferences getOfflinePreferences(UUID uuid, boolean privatePrefs, boolean readOnly, boolean dummy) { Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic; IWebPreferences prefs = preferences.get(uuid); if (prefs == null) { AbstractWebPreferences newPrefs = dummy ? new DummyOfflineWebPreferences(uuid, privatePrefs, readOnly) : new OfflineWebPreferences(uuid, privatePrefs, readOnly); this.allPreferences.add(newPrefs); preferences.put(uuid.toString(), newPrefs); prefs = newPrefs; } return prefs; } private String sanitiseUUID(String uuid) { if (uuid == null) { throw new InvalidUUIDException("The UUID was null"); } uuid = uuid.toLowerCase().replace("-", "").trim(); Matcher uuidPatternMatcher = WebPreferencesManager.uuidPattern.matcher(uuid); if (!uuidPatternMatcher.matches()) { throw new InvalidUUIDException("The specified string [" + uuid + "] is not a valid UUID"); } return uuid; } /** * Get the default preferences manager (kv.liteloader.com) * * @return default preferences manager */ public static WebPreferencesManager getDefault() { return WebPreferencesManager.get(WebPreferencesManager.DEFAULT_HOSTNAME); } /** * Get a preferences manager for the specified service hostname * * @param hostName service hostname (bare hostname only, no protocol) * @return preferences manager * @throws InvalidServiceException if the specified host name is invalid */ @SuppressWarnings("unused") public static WebPreferencesManager get(String hostName) throws InvalidServiceException { try { new URI(String.format("http://%s/", hostName)); } catch (URISyntaxException ex) { throw new InvalidServiceException("The specified service host was not valid: " + hostName, ex); } if (WebPreferencesManager.updateDeamon == null) { WebPreferencesManager.updateDeamon = new WebPreferencesUpdateDeamon(); LiteLoader.getInterfaceManager().registerListener(WebPreferencesManager.updateDeamon); } WebPreferencesManager manager = WebPreferencesManager.managers.get(hostName); if (manager == null) { Minecraft minecraft = Minecraft.getMinecraft(); Proxy proxy = minecraft.getProxy(); Session session = minecraft.getSession(); manager = new WebPreferencesManager(proxy, session, hostName); WebPreferencesManager.managers.put(hostName, manager); } return manager; } }