-
Notifications
You must be signed in to change notification settings - Fork 707
/
validate.js
146 lines (125 loc) · 4.11 KB
/
validate.js
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
Game.prototype.VERBOTEN = ['eval', 'prototype', 'delete', 'return', 'moveToNextLevel', 'removeItemFromMap'];
Game.prototype.allowedTime = 2000;
// We may want to have level-specific hidden validation rules in the future.
// var validationRulesByLevel = [ null ];
var DummyDisplay = function () {
this.clear = function () {};
this.drawAll = function () {};
this.drawObject = function () {};
};
Game.prototype.validators = {
validateAtLeastXObjects: function(map, num, type) {
var count = 0;
for (var x = 0; x < map.getWidth(); x++) {
for (var y = 0; y < map.getHeight(); y++) {
if (map.getGrid()[x][y].type === type) {
count++;
}
}
}
if (count < num) {
throw 'Not enough ' + type + 's on the map! Expected: ' + num + ', found: ' + count;
}
},
validateExactlyXManyObjects: function(map, num, type) {
var count = 0;
for (var x = 0; x < map.getWidth(); x++) {
for (var y = 0; y < map.getHeight(); y++) {
if (map.getGrid()[x][y].type === type) {
count++;
}
}
}
if (count != num) {
throw 'Wrong number of ' + type + 's on the map! Expected: ' + num + ', found: ' + count;
}
}
}
Game.prototype.validate = function(allCode, playerCode, preserveOutput) {
var game = this;
var validateAtLeastXObjects = this.validators.validateAtLeastXObjects;
var validateExactlyXManyObjects = this.validators.validateExactlyXManyObjects;
validateLevel = function () {};
if (!preserveOutput) {
this.output.clear();
}
try {
for (var i = 0; i < this.VERBOTEN.length; i++) {
var badWord = this.VERBOTEN[i];
if (playerCode.indexOf(badWord) > -1) {
throw 'You are not allowed to use ' + badWord + '!';
}
}
var display = this.display; var output = this.output;
var dummyMap = new Map(new DummyDisplay, this);
dummyMap.setProperties(this.editor.getProperties()['mapProperties']);
// modify the code to always check time to prevent infinite loops
allCode = $.map(allCode.split('\n'), function (line, i) {
return line.replace(/((for|while) .*){/g,
"startTime = Date.now();" +
"$1{" +
"if (Date.now() - startTime > " + game.allowedTime + ") {" +
"throw '[Line " + (i+1) + "] TimeOutException: Maximum loop execution time of " + game.allowedTime + " ms exceeded.';" +
"}");
}).join('\n');
// evaluate the code to get startLevel() and (opt) validateLevel() methods
eval(allCode);
// start the level on a dummy map to validate
startLevel(dummyMap);
if (typeof(validateLevel) != 'undefined') {
validateLevel(dummyMap);
}
return startLevel;
} catch (e) {
var exceptionText = e.toString();
if (e instanceof SyntaxError) {
if (lineNum = this.findSyntaxError(allCode, e.message)) {
exceptionText = "[Line " + lineNum + "] " + exceptionText;
}
}
this.display.appendError(exceptionText);
return null;
}
}
// makes sure nothing un-kosher happens during a callback within the game
// e.g. item collison; function phone
Game.prototype.validateCallback = function(callback) {
var validateAtLeastXObjects = this.validators.validateAtLeastXObjects;
var validateExactlyXManyObjects = this.validators.validateExactlyXManyObjects;
this.output.clear();
eval(this.editor.getGoodState()['code']); // get validateLevel method from last good state (if such a method exists)
try {
// run the callback
callback();
// check if validator still passes
try {
if (typeof(validateLevel) != 'undefined') {
validateLevel(this.map);
}
} catch (e) {
// validation failed - not much to do here but restart the level, unfortunately
alert('validateLevel failed!\n\n' + e.toString() + '\n\nRestarting level ...');
this.evalLevelCode();
}
// refresh the map, just in case
this.map.refresh();
} catch (e) {
this.display.appendError(e.toString());
}
}
// awful awful awful method that tries to find the line
// of code where a given error occurs
Game.prototype.findSyntaxError = function(code, errorMsg) {
var lines = code.split('\n');
for (var i = 1; i <= lines.length; i++) {
var testCode = lines.slice(0, i).join('\n');
try {
eval(testCode);
} catch (e) {
if (e.message == errorMsg) {
return i;
}
}
}
return null;
}