forked from tinyspeck/glitch-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LocalStorageRequester.as
386 lines (339 loc) · 11.5 KB
/
LocalStorageRequester.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
package {
/**
* Run this with wmode!=direct in order to preemptively get the
* Local Storage Security dialog to pop up and request some MBs
* of mems for the game.
*
* When we run the game with wmode=direct, the dialog doesn't appear.
*
* Pass it '?SWF_lso_needed=1024' to request 1kb.
*/
import com.tinyspeck.engine.util.EnvironmentUtil;
import flash.display.Sprite;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.events.UncaughtErrorEvent;
import flash.external.ExternalInterface;
import flash.net.SharedObject;
import flash.net.SharedObjectFlushStatus;
import flash.system.Capabilities;
import flash.utils.Timer;
import flash.utils.getQualifiedClassName;
import flash.utils.setTimeout;
public class LocalStorageRequester extends Sprite {
private static const TS_DATA_LATEST_VERSION:uint = 2;
private var _Q:Array = [];
private var _count:int = 0;
private var _default_timer_delay:int = 0;
private var _timer:Timer = new Timer(_default_timer_delay, 1);
public function LocalStorageRequester() {
var URLAndQSArgs:Object = EnvironmentUtil.getURLAndQSArgs();
var url:String = URLAndQSArgs.url;
var k:String;
// if they are using the debug player
if (Capabilities.isDebugger) {
// and not at a specific SWF_which
if (!(URLAndQSArgs.args.hasOwnProperty('SWF_which'))) {
// and not doing performance tests
if (url.indexOf('game/perf') == -1) {
// auto-redirect them to SWF_which=DEBUG
url+= '?SWF_which=DEBUG';
for (k in URLAndQSArgs.args) {
if (k == 'SWF_which') continue;
url+= '&'+k+'='+URLAndQSArgs.args[k];
}
// timeout or else there is a load never completed error
setTimeout(ExternalInterface.call, 1000, 'window.location.replace("'+url+'")');
return;
}
}
}
// Chrome has this nasty habit of firing:
// SecurityError: Error #2000: No active security context.
// Damned if I know or care why...
loaderInfo.uncaughtErrorEvents.addEventListener(
UncaughtErrorEvent.UNCAUGHT_ERROR, function (e:UncaughtErrorEvent):void {
Console.warn(e.toString() + ': ' + e.error);
e.preventDefault();
e.stopImmediatePropagation();
}, false, int.MAX_VALUE);
_timer.addEventListener(TimerEvent.TIMER, function (e:TimerEvent):void {
if (_Q.length) call_JS(_Q.shift());
});
try {
const fvd:FlashVarData = FlashVarData.createFlashVarData(this);
const sharedObject:SharedObject = SharedObject.getLocal('TS_DATA', '/');
// handle version control and migration
if (sharedObject.data['version'] != TS_DATA_LATEST_VERSION) {
switch (sharedObject.data['version']) {
default: {
migrateUndefined(sharedObject);
break;
}
case 1: {
migrate1To2(sharedObject);
break;
}
}
}
// we've done what we can and should be up-to-date
sharedObject.data.version = TS_DATA_LATEST_VERSION;
sharedObject.flush();
// how much memory do we need up-to
var needed:uint = (fvd.getFlashvar('SWF_lso_needed', 'Number') || 100*1024); // 100kb out to be enough for anyone
// adjust for the amount already in use and ask for 1 byte less
// (no off-by-ones on my watch, flash rounds up anyway)
needed = needed - sharedObject.size - 1;
Console.info('Requesting ' + needed + ' bytes of Flash Local Storage');
if (sharedObject.flush(needed) == SharedObjectFlushStatus.FLUSHED) {
Console.info('Storage request granted');
call_JS({meth: 'remove_lso'});
} else {
// flush pending, security dialog is raised
sharedObject.addEventListener(NetStatusEvent.NET_STATUS, function(e:NetStatusEvent):void {
// dialog has been dismissed
if (e.info.code == "SharedObject.Flush.Success") {
Console.info('Storage request granted');
call_JS({meth: 'remove_lso'});
} else {
Console.error('You denied giving Glitch local storage.');
call_JS({meth: 'lso_request_denied'});
}
sharedObject.removeEventListener(NetStatusEvent.NET_STATUS, arguments.callee);
});
}
} catch (e:Error) {
// security freaks
Console.error('You permanently disabled your Flash Local Storage, but Glitch wants some. No soup for you!\n\t' + e.toString());
call_JS({meth: 'lso_request_denied'});
}
}
private static function migrateUndefined(sharedObject:SharedObject):void {
clearSO(sharedObject);
}
private static function migrate1To2(sharedObject:SharedObject):void {
for each (var obj:* in sharedObject.data) {
if (isPCObject(obj)) {
// clear chat_history
obj['chat_history'] = null;
delete obj['chat_history'];
// add is_pc for future migration ease
obj['is_pc'] = true;
}
}
sharedObject.flush();
}
private static function isPCObject(obj:*):Boolean {
// obj is Object will return true for e.g. ByteArray, which is bad
return ((getQualifiedClassName(obj) == 'Object') &&
// sfx_volume will be a hint that this is a LocalStorage object
// since is_pc was introduced introduced in version 2
(Object(obj).hasOwnProperty('is_pc') || Object(obj).hasOwnProperty('sfx_volume')));
}
private static function clearSO(sharedObject:SharedObject):void {
try {
// for now, clear the fucker out
Console.info('clearing TS_DATA');
sharedObject.clear();
sharedObject.flush();
} catch (e:Error) {
// whatever, we tried
}
}
/* talk to JS */
private function call_JS(msg:Object):void {
//Console.log(4, 'call_JS meth '+(msg.meth || 'NO METH SPECIFIED')+'');
// it is not a lost and resent msg
if (!msg._count && msg._count != 0) {
if (msg.del) {
msg.del = (new Date().getTime()) - msg.del;
msg.diff = (msg.del - msg.exp);
}
msg._count = _count++;
} else {
msg.resent = 1;
}
//Console.log('AS calling JS: '+(msg.meth || 'METH NOT SPECIFIED') +' '+(msg._count));
var received:Boolean = false;
try {
received = ExternalInterface.call('TS.client.AS_interface.incoming_JS', msg);
} catch(e:SecurityError) {
Console.warn('_actually_call_JS Sec. error '+e)
}
if (received) {
// Console.log('JS reports received '+msg._count+': '+received);
} else {
Console.warn('JS reports received '+msg._count+': '+received);
}
_timer.reset();
if (received) {
_timer.delay = _default_timer_delay;
} else {
_Q.unshift(msg);
_timer.delay = _default_timer_delay+50;
}
_timer.start();
}
}
}
import flash.display.DisplayObject;
import flash.display.LoaderInfo;
import flash.external.ExternalInterface;
class FlashVarData {
private var data:Object;
public static function createFlashVarData(root:DisplayObject):FlashVarData {
return new FlashVarData(LoaderInfo(root.loaderInfo));
}
public function FlashVarData(loaderInfo:LoaderInfo) {
this.data = loaderInfo.parameters;
for (var item:String in data) {
data[item] = String(data[item]);
}
}
public function getFlashvar(k:String, t:String = 'String'):* {
var val:*;
if (t == 'Number') {
val = (!isNaN(data[k])) ? parseFloat(data[k]) : null;
} else if (t == 'String') {
val = data[k] || '';
} else {
val = data[k];
}
return val;
}
}
class Console {
private static var to_firebug:Boolean = true;
private static var to_trace:Boolean = true;
private static var priArray:Array;
private static var console_name_prefix:String = 'com.tinyspeck.debug::Console';
private static var ns_prefix:String = 'com.tinyspeck';
private static function deepTrace(obj:*, level:int=0): void {
var tabs:String = "";
for (var i:int = 0; i < level; i++) {
tabs += " ";
}
for (var prop:String in obj){
if(to_trace == true){
trace(tabs + "\"" + prop + "\":" + obj[prop]);
}
deepTrace(obj[prop], level + 1);
}
}
public static function priOK(pri:String):Boolean {
if (!priArray) return true;
if (pri == '0') return true;
if (priArray.indexOf('all') != -1) return true;
if (priArray.indexOf(pri) == -1) return false;
return true;
}
public static function functionName():String {
var func:String = '';
var loc:String = '';
var s:String = new Error().getStackTrace();
if (!s) return 'no_trace_available';
var A:Array = s.split('\n');
//at com.tinyspeck.control::BootController/bootStrap()[/Users/eric/Documents/workspace/clientNew/src/com/tinyspeck/control/BootController.as:50]
var d:int = 1; // start at second line, because the first contains just "Error at"
var temp_line:String;
while(d==1 || temp_line.indexOf(console_name_prefix) > -1) {
temp_line = A[d];
d++;
}
var i:int = temp_line.indexOf('at ');
var line:String = temp_line.substr(i+3);
var line_parts:Array = line.split('()');
func = line_parts[0]+'()';
func = func.split('/')[func.split('/').length-1];
loc = line_parts[1].substr(line_parts[1].lastIndexOf('/')+1).replace(']', '');
if (func.indexOf(ns_prefix) > -1) func = func.split('::')[1];
return loc+' '+func;
}
/**
* Writes a message to the console.
* You may pass as many arguments as you'd like, and they will be
* joined together in a space-delimited line.
* */
public static function log(pri:*, ...rest):void{
pri = pri.toString();
if (pri && !priOK(pri)) return;
sendWpri('log', pri, rest);
}
/**
* Writes a message to the console with the visual "warning" icon and
* color coding and a hyperlink to the line where it was called.
* */
public static function warn(...rest):void{
send("warn", rest);
}
/**
* Writes a message to the console with the visual "info" icon and
* color coding and a hyperlink to the line where it was called.
* */
public static function info(...rest):void{
send("info",rest);
}
/**
* Writes a message to the console with the visual "error" icon and
* color coding and a hyperlink to the line where it was called.
* */
public static function error(...rest):void{
var s:String = new Error().getStackTrace();
s = s || 'no_trace_available';
send("error",rest);
send("log", 'STACK TRACE: '+s);
}
/**
* makes the call to console
* */
private static function sendWpri(level:String, pri:String = '', ...rest):void{
var f:String = functionName();
if (level == 'dir') {
if (to_trace == true) {
trace(to_trace);
trace('Showing tree '+rest.length+' ---> '+f);
deepTrace(rest);
}
if (to_firebug) {
var msg_type:String = (rest[0] && 'type' in rest[0]) ? ' for type:'+rest[0].type : '';
ExternalInterface.call("console.log", pri+' '+f+' >>> Showing tree'+msg_type+'');
try {ExternalInterface.call("console.dir", rest[0]);} catch(e:SecurityError) {error('call to console.dir failed')};
}
} else {
//rest.push(f);
if (to_trace == true){
trace(level.toUpperCase()+' '+pri+' '+f+' >>> '+rest[0]);
}
try {
if (to_firebug) {
ExternalInterface.call("console."+level, ((level == 'info' || level == 'warn')?'\n':'')+pri+' '+f+' >>> '+rest[0]);
}
} catch(e:SecurityError) {}
}
}
private static function send(level:String,...rest):void{
var f:String = functionName();
if (level == 'dir') {
if (to_trace == true){
deepTrace(rest);
}
if (to_firebug) {
try {
ExternalInterface.call("console.dir", rest[0]);
} catch(e:SecurityError) {
error('call to console.dir failed')
};
}
} else {
//rest.push(f);
if (to_trace == true){
trace(level.toUpperCase()+' '+rest[0]+' ---> '+f);
}
try {
if (to_firebug) {
ExternalInterface.call("console."+level, ((level == 'info' || level == 'warn')?'\n':'')+f+' >>> '+rest[0]);
}
} catch(e:SecurityError) {}
}
}
}