Commit c2d5a299069eb51531c306409567859dee6f9947

Authored by Mumfrey
1 parent b98d2088

Add offline support to webprefs, closes #7

src/client/java/com/mumfrey/liteloader/client/mixin/MixinSession.java
@@ -5,8 +5,6 @@ @@ -5,8 +5,6 @@
5 */ 5 */
6 package com.mumfrey.liteloader.client.mixin; 6 package com.mumfrey.liteloader.client.mixin;
7 7
8 -import java.util.UUID;  
9 -  
10 import org.spongepowered.asm.mixin.Mixin; 8 import org.spongepowered.asm.mixin.Mixin;
11 import org.spongepowered.asm.mixin.Shadow; 9 import org.spongepowered.asm.mixin.Shadow;
12 import org.spongepowered.asm.mixin.injection.At; 10 import org.spongepowered.asm.mixin.injection.At;
@@ -30,7 +28,6 @@ public abstract class MixinSession @@ -30,7 +28,6 @@ public abstract class MixinSession
30 )) 28 ))
31 private void generateGameProfile(CallbackInfoReturnable<GameProfile> ci) 29 private void generateGameProfile(CallbackInfoReturnable<GameProfile> ci)
32 { 30 {
33 - UUID uuid = EntityPlayer.getUUID(new GameProfile((UUID)null, this.getUsername()));  
34 - ci.setReturnValue(new GameProfile(uuid, this.getUsername())); 31 + ci.setReturnValue(new GameProfile(EntityPlayer.getOfflineUUID(this.getUsername()), this.getUsername()));
35 } 32 }
36 } 33 }
src/client/java/com/mumfrey/webprefs/AbstractWebPreferences.java 0 → 100644
  1 +package com.mumfrey.webprefs;
  2 +
  3 +import java.util.Set;
  4 +import java.util.UUID;
  5 +
  6 +import com.mumfrey.webprefs.interfaces.IWebPreferences;
  7 +
  8 +/**
  9 + * Common base class for online/offline web preferences
  10 + */
  11 +abstract class AbstractWebPreferences implements IWebPreferences
  12 +{
  13 + /**
  14 + * Our UUID
  15 + */
  16 + protected final String uuid;
  17 +
  18 + /**
  19 + * True if we are a private settings set
  20 + */
  21 + protected final boolean isPrivate;
  22 +
  23 + protected final boolean isReadOnly;
  24 +
  25 + AbstractWebPreferences(UUID uuid, boolean isPrivate, boolean isReadOnly)
  26 + {
  27 + this(uuid.toString(), isPrivate, isReadOnly);
  28 + }
  29 +
  30 + AbstractWebPreferences(String uuid, boolean isPrivate, boolean isReadOnly)
  31 + {
  32 + this.uuid = uuid;
  33 + this.isPrivate = isPrivate;
  34 + this.isReadOnly = isReadOnly;
  35 + }
  36 +
  37 + void onTick()
  38 + {
  39 + // stub for subclasses
  40 + }
  41 +
  42 + /* (non-Javadoc)
  43 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#getUUID()
  44 + */
  45 + @Override
  46 + public final String getUUID()
  47 + {
  48 + return this.uuid;
  49 + }
  50 +
  51 + /* (non-Javadoc)
  52 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isPrivate()
  53 + */
  54 + @Override
  55 + public final boolean isPrivate()
  56 + {
  57 + return this.isPrivate;
  58 + }
  59 +
  60 + /* (non-Javadoc)
  61 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isReadOnly()
  62 + */
  63 + @Override
  64 + public final boolean isReadOnly()
  65 + {
  66 + return this.isReadOnly;
  67 + }
  68 +
  69 + /* (non-Javadoc)
  70 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  71 + * #request(java.lang.String)
  72 + */
  73 + @Override
  74 + public void request(String key)
  75 + {
  76 + WebPreferences.validateKey(key);
  77 + }
  78 +
  79 + /* (non-Javadoc)
  80 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  81 + * #request(java.lang.String[])
  82 + */
  83 + @Override
  84 + public void request(String... keys)
  85 + {
  86 + if (keys == null || keys.length < 1) return;
  87 + if (keys.length == 1) this.request(keys[0]);
  88 +
  89 + for (String key : keys)
  90 + {
  91 + this.request(key);
  92 + }
  93 + }
  94 +
  95 + /* (non-Javadoc)
  96 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  97 + * #request(java.util.Set)
  98 + */
  99 + @Override
  100 + public void request(Set<String> keys)
  101 + {
  102 + if (keys == null || keys.size() < 1) return;
  103 +
  104 + for (String key : keys)
  105 + {
  106 + this.request(key);
  107 + }
  108 + }
  109 +
  110 + /* (non-Javadoc)
  111 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#commit(boolean)
  112 + */
  113 + @Override
  114 + public void commit(boolean force)
  115 + {
  116 + }
  117 +
  118 + /* (non-Javadoc)
  119 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  120 + * #set(java.lang.String, java.lang.String)
  121 + */
  122 + @Override
  123 + public void set(String key, String value)
  124 + {
  125 + WebPreferences.validateKey(key);
  126 + }
  127 +
  128 + /* (non-Javadoc)
  129 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  130 + * #remove(java.lang.String)
  131 + */
  132 + @Override
  133 + public void remove(String key)
  134 + {
  135 + this.set(key, "");
  136 + }
  137 +}
src/client/java/com/mumfrey/webprefs/DummyOfflineWebPreferences.java 0 → 100644
  1 +package com.mumfrey.webprefs;
  2 +
  3 +import java.util.UUID;
  4 +
  5 +/**
  6 + * No-op webpreferences set which is returned for nonlocal players when the
  7 + * client is running in offline mode.
  8 + */
  9 +public class DummyOfflineWebPreferences extends AbstractWebPreferences
  10 +{
  11 + DummyOfflineWebPreferences(UUID uuid, boolean isPrivate, boolean isReadOnly)
  12 + {
  13 + this(uuid.toString(), isPrivate, isReadOnly);
  14 + }
  15 +
  16 + public DummyOfflineWebPreferences(String uuid, boolean isPrivate, boolean isReadOnly)
  17 + {
  18 + super(uuid, isPrivate, isReadOnly);
  19 + }
  20 +
  21 + /* (non-Javadoc)
  22 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#poll()
  23 + */
  24 + @Override
  25 + public void poll()
  26 + {
  27 + }
  28 +
  29 + /* (non-Javadoc)
  30 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  31 + * #has(java.lang.String)
  32 + */
  33 + @Override
  34 + public boolean has(String key)
  35 + {
  36 + WebPreferences.validateKey(key);
  37 + return false;
  38 + }
  39 +
  40 + /* (non-Javadoc)
  41 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  42 + * #get(java.lang.String)
  43 + */
  44 + @Override
  45 + public String get(String key)
  46 + {
  47 + WebPreferences.validateKey(key);
  48 + return "";
  49 + }
  50 +
  51 + /* (non-Javadoc)
  52 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  53 + * #get(java.lang.String, java.lang.String)
  54 + */
  55 + @Override
  56 + public String get(String key, String defaultValue)
  57 + {
  58 + WebPreferences.validateKey(key);
  59 + return defaultValue;
  60 + }
  61 +}
0 \ No newline at end of file 62 \ No newline at end of file
src/client/java/com/mumfrey/webprefs/OfflineWebPreferences.java 0 → 100644
  1 +package com.mumfrey.webprefs;
  2 +
  3 +import java.io.File;
  4 +import java.io.FileReader;
  5 +import java.io.FileWriter;
  6 +import java.io.IOException;
  7 +import java.util.HashMap;
  8 +import java.util.Map;
  9 +import java.util.UUID;
  10 +
  11 +import com.google.gson.Gson;
  12 +import com.google.gson.GsonBuilder;
  13 +import com.mumfrey.liteloader.core.LiteLoader;
  14 +import com.mumfrey.webprefs.exceptions.ReadOnlyPreferencesException;
  15 +
  16 +/**
  17 + * Surrogate preferences returned for a local offline player, allows offline
  18 + * players to use the webpreferences system with preferences just being stored
  19 + * locally.
  20 + */
  21 +class OfflineWebPreferences extends DummyOfflineWebPreferences
  22 +{
  23 + /**
  24 + * Gson instance for serialisation/deserialisation to local JSON file
  25 + */
  26 + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
  27 +
  28 + /**
  29 + * Maximum commit-to-disk rate in ticks
  30 + */
  31 + private static final int COMMIT_RATE = 20 * 3;
  32 +
  33 + /**
  34 + * JSON file
  35 + */
  36 + private final File store;
  37 +
  38 + /**
  39 + * Local KV store
  40 + */
  41 + final Map<String, String> prefs;
  42 +
  43 + /**
  44 + * Tick number for write throttling
  45 + */
  46 + private int tickNumber;
  47 +
  48 + /**
  49 + * Flag indicating serialisation to disk is required
  50 + */
  51 + private boolean isDirty;
  52 +
  53 + OfflineWebPreferences(UUID uuid, boolean isPrivate, boolean isReadOnly)
  54 + {
  55 + this(uuid.toString(), isPrivate, isReadOnly);
  56 + }
  57 +
  58 + OfflineWebPreferences(String uuid, boolean isPrivate, boolean isReadOnly)
  59 + {
  60 + super(uuid, isPrivate, isReadOnly);
  61 +
  62 + this.store = new File(LiteLoader.getCommonConfigFolder(), String.format("%s.%sprefs.json", uuid, isPrivate ? "private" : ""));
  63 + this.prefs = this.loadValues();
  64 + }
  65 +
  66 + @SuppressWarnings("unchecked")
  67 + private Map<String, String> loadValues()
  68 + {
  69 + if (this.store.isFile())
  70 + {
  71 + FileReader reader = null;
  72 +
  73 + try
  74 + {
  75 + reader = new FileReader(this.store);
  76 + return OfflineWebPreferences.gson.fromJson(reader, Map.class);
  77 + }
  78 + catch (IOException ex) {}
  79 + finally
  80 + {
  81 + try
  82 + {
  83 + if (reader != null) reader.close();
  84 + }
  85 + catch (IOException ex) {}
  86 + }
  87 + }
  88 +
  89 + return new HashMap<String, String>();
  90 + }
  91 +
  92 + private void saveValues()
  93 + {
  94 + FileWriter writer = null;
  95 +
  96 + try
  97 + {
  98 + writer = new FileWriter(this.store);
  99 + OfflineWebPreferences.gson.toJson(this.prefs, writer);
  100 + }
  101 + catch (IOException ex) {}
  102 + finally
  103 + {
  104 + try
  105 + {
  106 + if (writer != null) writer.close();
  107 + }
  108 + catch (IOException ex) {}
  109 + }
  110 + }
  111 +
  112 + @Override
  113 + void onTick()
  114 + {
  115 + if (this.tickNumber++ > OfflineWebPreferences.COMMIT_RATE && this.isDirty)
  116 + {
  117 + this.isDirty = false;
  118 + this.tickNumber = 0;
  119 + this.saveValues();
  120 + }
  121 + }
  122 +
  123 + @Override
  124 + public void request(String key)
  125 + {
  126 + WebPreferences.validateKey(key);
  127 + if (!this.prefs.containsKey(key))
  128 + {
  129 + this.prefs.put(key, "");
  130 + }
  131 + }
  132 +
  133 + /* (non-Javadoc)
  134 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#commit(boolean)
  135 + */
  136 + @Override
  137 + public void commit(boolean force)
  138 + {
  139 + this.isDirty = true;
  140 + }
  141 +
  142 + /* (non-Javadoc)
  143 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  144 + * #has(java.lang.String)
  145 + */
  146 + @Override
  147 + public boolean has(String key)
  148 + {
  149 + WebPreferences.validateKey(key);
  150 + return this.prefs.containsKey(key);
  151 + }
  152 +
  153 + /* (non-Javadoc)
  154 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  155 + * #get(java.lang.String)
  156 + */
  157 + @Override
  158 + public String get(String key)
  159 + {
  160 + return this.get(key, "");
  161 + }
  162 +
  163 + /* (non-Javadoc)
  164 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  165 + * #get(java.lang.String, java.lang.String)
  166 + */
  167 + @Override
  168 + public String get(String key, String defaultValue)
  169 + {
  170 + WebPreferences.validateKey(key);
  171 + String value = this.prefs.get(key);
  172 + return value != null ? value : defaultValue;
  173 + }
  174 +
  175 + /* (non-Javadoc)
  176 + * @see com.mumfrey.webprefs.interfaces.IWebPreferences
  177 + * #set(java.lang.String, java.lang.String)
  178 + */
  179 + @Override
  180 + public void set(String key, String value)
  181 + {
  182 + if (this.isReadOnly())
  183 + {
  184 + throw new ReadOnlyPreferencesException("Preference collection for " + this.uuid + " is read-only");
  185 + }
  186 +
  187 + WebPreferences.validateKV(key, value);
  188 +
  189 + this.prefs.put(key, value);
  190 + this.isDirty = true;
  191 + }
  192 +}
src/client/java/com/mumfrey/webprefs/WebPreferences.java
@@ -4,6 +4,7 @@ import java.util.HashMap; @@ -4,6 +4,7 @@ import java.util.HashMap;
4 import java.util.HashSet; 4 import java.util.HashSet;
5 import java.util.Map; 5 import java.util.Map;
6 import java.util.Set; 6 import java.util.Set;
  7 +import java.util.UUID;
7 import java.util.concurrent.ConcurrentHashMap; 8 import java.util.concurrent.ConcurrentHashMap;
8 import java.util.regex.Pattern; 9 import java.util.regex.Pattern;
9 10
@@ -12,11 +13,10 @@ import com.mumfrey.webprefs.exceptions.InvalidKeyException; @@ -12,11 +13,10 @@ import com.mumfrey.webprefs.exceptions.InvalidKeyException;
12 import com.mumfrey.webprefs.exceptions.InvalidValueException; 13 import com.mumfrey.webprefs.exceptions.InvalidValueException;
13 import com.mumfrey.webprefs.exceptions.ReadOnlyPreferencesException; 14 import com.mumfrey.webprefs.exceptions.ReadOnlyPreferencesException;
14 import com.mumfrey.webprefs.framework.RequestFailureReason; 15 import com.mumfrey.webprefs.framework.RequestFailureReason;
15 -import com.mumfrey.webprefs.interfaces.IWebPreferences;  
16 import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; 16 import com.mumfrey.webprefs.interfaces.IWebPreferencesClient;
17 import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; 17 import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider;
18 18
19 -class WebPreferences implements IWebPreferences 19 +class WebPreferences extends AbstractWebPreferences
20 { 20 {
21 /** 21 /**
22 * The update frequency to use when operating normally, this is the 22 * The update frequency to use when operating normally, this is the
@@ -104,18 +104,6 @@ class WebPreferences implements IWebPreferences @@ -104,18 +104,6 @@ class WebPreferences implements IWebPreferences
104 private final IWebPreferencesClient client; 104 private final IWebPreferencesClient client;
105 105
106 /** 106 /**
107 - * Our UUID  
108 - */  
109 - protected final String uuid;  
110 -  
111 - /**  
112 - * True if we are a private settings set  
113 - */  
114 - protected final boolean isPrivate;  
115 -  
116 - protected final boolean isReadOnly;  
117 -  
118 - /**  
119 * Current key/value pairs 107 * Current key/value pairs
120 */ 108 */
121 protected final Map<String, String> prefs = new ConcurrentHashMap<String, String>(); 109 protected final Map<String, String> prefs = new ConcurrentHashMap<String, String>();
@@ -149,61 +137,30 @@ class WebPreferences implements IWebPreferences @@ -149,61 +137,30 @@ class WebPreferences implements IWebPreferences
149 private volatile int updateCheckTimer = 1; 137 private volatile int updateCheckTimer = 1;
150 138
151 protected int requestTimeoutTimer = 0; 139 protected int requestTimeoutTimer = 0;
  140 +
  141 + WebPreferences(IWebPreferencesProvider provider, UUID uuid, boolean isPrivate, boolean isReadOnly)
  142 + {
  143 + this(provider, uuid.toString(), isPrivate, isReadOnly);
  144 + }
152 145
153 WebPreferences(IWebPreferencesProvider provider, String uuid, boolean isPrivate, boolean isReadOnly) 146 WebPreferences(IWebPreferencesProvider provider, String uuid, boolean isPrivate, boolean isReadOnly)
154 { 147 {
  148 + super(uuid, isPrivate, isReadOnly);
155 this.provider = provider; 149 this.provider = provider;
156 - this.uuid = uuid;  
157 - this.isPrivate = isPrivate;  
158 - this.isReadOnly = isReadOnly;  
159 this.client = new Client(); 150 this.client = new Client();
160 } 151 }
161 152
162 - /* (non-Javadoc)  
163 - * @see com.mumfrey.webprefs.interfaces.IWebPreferences#getUUID()  
164 - */  
165 - @Override  
166 - public String getUUID()  
167 - {  
168 - return this.uuid;  
169 - }  
170 -  
171 - /* (non-Javadoc)  
172 - * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isPrivate()  
173 - */  
174 - @Override  
175 - public boolean isPrivate()  
176 - {  
177 - return this.isPrivate;  
178 - }  
179 -  
180 - /* (non-Javadoc)  
181 - * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isReadOnly()  
182 - */  
183 @Override 153 @Override
184 - public boolean isReadOnly()  
185 - {  
186 - return this.isReadOnly;  
187 - }  
188 -  
189 void onTick() 154 void onTick()
190 { 155 {
191 - if (this.updateCheckTimer > 0) 156 + if (this.updateCheckTimer > 0 && --this.updateCheckTimer < 1)
192 { 157 {
193 - this.updateCheckTimer--;  
194 - if (this.updateCheckTimer < 1)  
195 - {  
196 - this.update();  
197 - } 158 + this.update();
198 } 159 }
199 160
200 - if (this.requestTimeoutTimer > 0) 161 + if (this.requestTimeoutTimer > 0 && --this.requestTimeoutTimer < 1)
201 { 162 {
202 - this.requestTimeoutTimer--;  
203 - if (this.requestTimeoutTimer < 1)  
204 - {  
205 - this.handleTimeout();  
206 - } 163 + this.handleTimeout();
207 } 164 }
208 } 165 }
209 166
@@ -449,16 +406,6 @@ class WebPreferences implements IWebPreferences @@ -449,16 +406,6 @@ class WebPreferences implements IWebPreferences
449 } 406 }
450 } 407 }
451 408
452 - /* (non-Javadoc)  
453 - * @see com.mumfrey.webprefs.interfaces.IWebPreferences  
454 - * #remove(java.lang.String)  
455 - */  
456 - @Override  
457 - public void remove(String key)  
458 - {  
459 - this.set(key, "");  
460 - }  
461 -  
462 /** 409 /**
463 * Add a key to the current request set, the key will be requested from the 410 * Add a key to the current request set, the key will be requested from the
464 * server on the next {@link #update()} 411 * server on the next {@link #update()}
@@ -563,7 +510,7 @@ class WebPreferences implements IWebPreferences @@ -563,7 +510,7 @@ class WebPreferences implements IWebPreferences
563 /** 510 /**
564 * @param key 511 * @param key
565 */ 512 */
566 - protected static final void validateKey(String key) 513 + protected static void validateKey(String key)
567 { 514 {
568 if (key == null || !WebPreferences.keyPattern.matcher(key).matches()) 515 if (key == null || !WebPreferences.keyPattern.matcher(key).matches())
569 { 516 {
@@ -575,7 +522,7 @@ class WebPreferences implements IWebPreferences @@ -575,7 +522,7 @@ class WebPreferences implements IWebPreferences
575 * @param key 522 * @param key
576 * @param value 523 * @param value
577 */ 524 */
578 - protected static final void validateKV(String key, String value) 525 + protected static void validateKV(String key, String value)
579 { 526 {
580 WebPreferences.validateKey(key); 527 WebPreferences.validateKey(key);
581 528
src/client/java/com/mumfrey/webprefs/WebPreferencesManager.java
@@ -130,7 +130,7 @@ public final class WebPreferencesManager @@ -130,7 +130,7 @@ public final class WebPreferencesManager
130 /** 130 /**
131 * All preference sets, for iteration purposes 131 * All preference sets, for iteration purposes
132 */ 132 */
133 - private final List<WebPreferences> allPreferences = new LinkedList<WebPreferences>(); 133 + private final List<AbstractWebPreferences> allPreferences = new LinkedList<AbstractWebPreferences>();
134 134
135 /** 135 /**
136 * All public preference sets, mapped by UUID 136 * All public preference sets, mapped by UUID
@@ -152,7 +152,7 @@ public final class WebPreferencesManager @@ -152,7 +152,7 @@ public final class WebPreferencesManager
152 { 152 {
153 this.provider.onTick(); 153 this.provider.onTick();
154 154
155 - for (WebPreferences prefs : this.allPreferences) 155 + for (AbstractWebPreferences prefs : this.allPreferences)
156 { 156 {
157 try 157 try
158 { 158 {
@@ -164,7 +164,7 @@ public final class WebPreferencesManager @@ -164,7 +164,7 @@ public final class WebPreferencesManager
164 164
165 void onJoinGame() 165 void onJoinGame()
166 { 166 {
167 - for (WebPreferences prefs : this.allPreferences) 167 + for (AbstractWebPreferences prefs : this.allPreferences)
168 { 168 {
169 try 169 try
170 { 170 {
@@ -176,7 +176,9 @@ public final class WebPreferencesManager @@ -176,7 +176,9 @@ public final class WebPreferencesManager
176 176
177 177
178 /** 178 /**
179 - * Get a public or private preferences collection for the local player 179 + * Get a public or private preferences collection for the local player. If
  180 + * the game is running in offline mode, a local preference collection is
  181 + * returned instead.
180 * 182 *
181 * @param privatePrefs true to fetch the player's private preferences, false 183 * @param privatePrefs true to fetch the player's private preferences, false
182 * to fetch public preferences 184 * to fetch public preferences
@@ -184,11 +186,21 @@ public final class WebPreferencesManager @@ -184,11 +186,21 @@ public final class WebPreferencesManager
184 */ 186 */
185 public IWebPreferences getLocalPreferences(boolean privatePrefs) 187 public IWebPreferences getLocalPreferences(boolean privatePrefs)
186 { 188 {
187 - return this.getPreferences(this.session.getPlayerID(), privatePrefs); 189 + try
  190 + {
  191 + return this.getPreferences(this.session.getPlayerID(), privatePrefs);
  192 + }
  193 + catch (InvalidUUIDException ex)
  194 + {
  195 + UUID offlineUUID = EntityPlayer.getOfflineUUID(this.session.getUsername());
  196 + return this.getOfflinePreferences(offlineUUID, privatePrefs, false, false);
  197 + }
188 } 198 }
189 199
190 /** 200 /**
191 - * Get a public preferences collection for the specified player. 201 + * Get a public preferences collection for the specified player. If the game
  202 + * is running in offline mode, a dummy preference collection supporting no
  203 + * operations is returned instead.
192 * 204 *
193 * @param player Player to fetch preferences for 205 * @param player Player to fetch preferences for
194 * @param privatePrefs True to fetch the player's private preferences, false 206 * @param privatePrefs True to fetch the player's private preferences, false
@@ -198,7 +210,16 @@ public final class WebPreferencesManager @@ -198,7 +210,16 @@ public final class WebPreferencesManager
198 */ 210 */
199 public IWebPreferences getPreferences(EntityPlayer player) 211 public IWebPreferences getPreferences(EntityPlayer player)
200 { 212 {
201 - return this.getPreferences(player, false); 213 + try
  214 + {
  215 + return this.getPreferences(player, false);
  216 + }
  217 + catch (InvalidUUIDException ex)
  218 + {
  219 + String playerName = player.getName();
  220 + UUID offlineUUID = EntityPlayer.getOfflineUUID(playerName);
  221 + return this.getOfflinePreferences(offlineUUID, false, false, !playerName.equals(this.session.getUsername()));
  222 + }
202 } 223 }
203 224
204 /** 225 /**
@@ -279,7 +300,6 @@ public final class WebPreferencesManager @@ -279,7 +300,6 @@ public final class WebPreferencesManager
279 uuid = this.sanitiseUUID(uuid); 300 uuid = this.sanitiseUUID(uuid);
280 301
281 Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic; 302 Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic;
282 -  
283 IWebPreferences prefs = preferences.get(uuid); 303 IWebPreferences prefs = preferences.get(uuid);
284 304
285 if (prefs == null) 305 if (prefs == null)
@@ -293,6 +313,24 @@ public final class WebPreferencesManager @@ -293,6 +313,24 @@ public final class WebPreferencesManager
293 return prefs; 313 return prefs;
294 } 314 }
295 315
  316 + private IWebPreferences getOfflinePreferences(UUID uuid, boolean privatePrefs, boolean readOnly, boolean dummy)
  317 + {
  318 + Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic;
  319 + IWebPreferences prefs = preferences.get(uuid);
  320 +
  321 + if (prefs == null)
  322 + {
  323 + AbstractWebPreferences newPrefs = dummy
  324 + ? new DummyOfflineWebPreferences(uuid, privatePrefs, readOnly)
  325 + : new OfflineWebPreferences(uuid, privatePrefs, readOnly);
  326 + this.allPreferences.add(newPrefs);
  327 + preferences.put(uuid.toString(), newPrefs);
  328 + prefs = newPrefs;
  329 + }
  330 +
  331 + return prefs;
  332 + }
  333 +
296 private String sanitiseUUID(String uuid) 334 private String sanitiseUUID(String uuid)
297 { 335 {
298 if (uuid == null) 336 if (uuid == null)