Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOCheckShipDataPListVerifierStage.m
Go to the documentation of this file.
1/*
2
3OOCheckShipDataPListVerifierStage.m
4
5
6Oolite
7Copyright (C) 2004-2013 Giles C Williams and contributors
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22MA 02110-1301, USA.
23
24*/
25
28
29#if OO_OXP_VERIFIER_ENABLED
30
32#import "OOStringParsing.h"
33#import "ResourceManager.h"
35#import "OOStringParsing.h"
38
39static NSString * const kStageName = @"Checking shipdata.plist";
40
41
42@interface OOCheckShipDataPListVerifierStage (OOPrivate)
43
44- (void)verifyShipInfo:(NSDictionary *)info withName:(NSString *)name;
45
46- (void)message:(NSString *)format, ...;
47- (void)verboseMessage:(NSString *)format, ...;
48
49- (void)getRoles;
50- (void)checkKeys;
51- (void)checkSchema;
52- (void)checkModel;
53
54- (NSSet *)rolesFromString:(NSString *)string;
55
56@end
57
58
60
61- (NSString *)name
62{
63 return kStageName;
64}
65
66
67- (NSSet *)dependents
68{
69 NSMutableSet *result = [[super dependents] mutableCopy];
70 [result addObject:[OOModelVerifierStage nameForReverseDependencyForVerifier:[self verifier]]];
71 [result addObject:[OOAIStateMachineVerifierStage nameForReverseDependencyForVerifier:[self verifier]]];
72 return [result autorelease];
73}
74
75
76- (BOOL)shouldRun
77{
78 OOFileScannerVerifierStage *fileScanner = nil;
79
80 fileScanner = [[self verifier] fileScannerStage];
81 return [fileScanner fileExists:@"shipdata.plist"
82 inFolder:@"Config"
84 checkBuiltIn:NO];
85}
86
87
88- (void)run
89{
90 OOFileScannerVerifierStage *fileScanner = nil;
91 NSAutoreleasePool *pool = nil;
92 NSEnumerator *shipEnum = nil;
93 NSString *shipKey = nil;
94 NSDictionary *shipInfo = nil;
95 NSDictionary *ooliteShipData = nil;
96 NSDictionary *settings = nil;
97 NSMutableSet *mergeSet = nil;
98 NSArray *shipList = nil;
99
100 fileScanner = [[self verifier] fileScannerStage];
101 _shipdataPList = [fileScanner plistNamed:@"shipdata.plist"
102 inFolder:@"Config"
104 checkBuiltIn:NO];
105
106 if (_shipdataPList == nil) return;
107
108 // Get AI verifier stage (may be nil).
109 _aiVerifierStage = [[self verifier] stageWithName:[OOAIStateMachineVerifierStage nameForReverseDependencyForVerifier:[self verifier]]];
110
111 ooliteShipData = [ResourceManager dictionaryFromFilesNamed:@"shipdata.plist"
112 inFolder:@"Config"
113 andMerge:YES];
114
115 // Check that it's a dictionary
116 if (![_shipdataPList isKindOfClass:[NSDictionary class]])
117 {
118 OOLog(@"verifyOXP.shipdataPList.notDict", @"%@", @"***** ERROR: shipdata.plist is not a dictionary.");
119 return;
120 }
121
122 // Keys that apply to all ships
123 _ooliteShipNames = [NSSet setWithArray:[ooliteShipData allKeys]];
124 settings = [[self verifier] configurationDictionaryForKey:@"shipdataPListSettings"];
125 _basicKeys = [settings oo_setForKey:@"knownShipKeys"];
126
127 // Keys that apply to stations/carriers
128 mergeSet = [_basicKeys mutableCopy];
129 [mergeSet addObjectsFromArray:[settings oo_arrayForKey:@"knownStationKeys"]];
130 _stationKeys = mergeSet;
131
132 // Keys that apply to player ships
133 mergeSet = [_basicKeys mutableCopy];
134 [mergeSet addObjectsFromArray:[settings oo_arrayForKey:@"knownPlayerKeys"]];
135 _playerKeys = [[mergeSet copy] autorelease];
136
137 // Keys that apply to _any_ ship -- union of the above
138 [mergeSet unionSet:_stationKeys];
139 _allKeys = mergeSet;
140
141 _schemaVerifier = [OOPListSchemaVerifier verifierWithSchema:[ResourceManager dictionaryFromFilesNamed:@"shipdataEntrySchema.plist" inFolder:@"Schemata" andMerge:NO]];
142 [_schemaVerifier setDelegate:self];
143
144 shipList = [[_shipdataPList allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
145 for (shipEnum = [shipList objectEnumerator]; (shipKey = [shipEnum nextObject]); )
146 {
147 pool = [[NSAutoreleasePool alloc] init];
148
149 shipInfo = [_shipdataPList oo_dictionaryForKey:shipKey];
150 if (shipInfo == nil)
151 {
152 OOLog(@"verifyOXP.shipdata.badType", @"***** ERROR: shipdata.plist entry for \"%@\" is not a dictionary.", shipKey);
153 }
154 else
155 {
156 [self verifyShipInfo:shipInfo withName:shipKey];
157 }
158
159 [pool release];
160 }
161
162 _shipdataPList = nil;
163 _ooliteShipNames = nil;
164 _basicKeys = nil;
165 _stationKeys = nil;
166 _playerKeys = nil;
167}
168
169@end
170
171
172@implementation OOCheckShipDataPListVerifierStage (OOPrivate)
173
174- (void)verifyShipInfo:(NSDictionary *)info withName:(NSString *)name
175{
176 _name = name;
177 _info = info;
178 _havePrintedMessage = NO;
180
181 [self getRoles];
182 [self checkKeys];
183 [self checkSchema];
184 [self checkModel];
185
186 NSString *aiName = [info oo_stringForKey:@"ai_type"];
187 if (aiName != nil)
188 {
189 if (![aiName hasSuffix:@".js"])
190 {
191 [_aiVerifierStage stateMachineNamed:aiName usedByShip:name];
192 }
193 }
194
195 // Todo: check for pirates with 0 bounty
196
198 if (!_havePrintedMessage)
199 {
200 OOLog(@"verifyOXP.verbose.shipData.OK", @"- ship \"%@\" OK.", _name);
201 }
202 _name = nil;
203 _info = nil;
204 _roles = nil;
205}
206
207
208// Custom log method to group messages by ship.
209- (void)message:(NSString *)format, ...
210{
211 va_list args;
212
213 if (!_havePrintedMessage)
214 {
215 OOLog(@"verifyOXP.shipData.firstMessage", @"Ship \"%@\":", _name);
216 OOLogIndent();
217 _havePrintedMessage = YES;
218 }
219
220 va_start(args, format);
221 OOLogWithFunctionFileAndLineAndArguments(@"verifyOXP.shipData", NULL, NULL, 0, format, args);
222 va_end(args);
223}
224
225
226- (void)verboseMessage:(NSString *)format, ...
227{
228 va_list args;
229
230 if (!OOLogWillDisplayMessagesInClass(@"verifyOXP.verbose.shipData")) return;
231
232 if (!_havePrintedMessage)
233 {
234 OOLog(@"verifyOXP.shipData.firstMessage", @"Ship \"%@\":", _name);
235 OOLogIndent();
236 _havePrintedMessage = YES;
237 }
238
239 va_start(args, format);
240 OOLogWithFunctionFileAndLineAndArguments(@"verifyOXP.verbose.shipData", NULL, NULL, 0, format, args);
241 va_end(args);
242}
243
244
245- (void)getRoles
246{
247 NSString *rolesString = nil;
248
249 rolesString = [_info objectForKey:@"roles"];
250 _roles = [self rolesFromString:rolesString];
251 _isPlayer = [_roles containsObject:@"player"];
252 _isStation = [_info oo_boolForKey:@"is_carrier" defaultValue:NO] ||
253 [_info oo_boolForKey:@"isCarrier" defaultValue:NO] ||
254 [rolesString rangeOfString:@"station"].location != NSNotFound ||
255 [rolesString rangeOfString:@"carrier"].location != NSNotFound;
256 // the is_carrier or isCarrier key will be missed when it was insise a like_ship definition.
257 _isTemplate = [_info oo_boolForKey:@"is_template" defaultValue:NO];
258
259 if (_isPlayer && _isStation)
260 {
261 [self message:@"***** ERROR: ship is both a player ship and a station. Treating as non-station."];
262 _isStation = NO;
263 }
264}
265
266
267- (void)checkKeys
268{
269 NSSet *referenceSet = nil;
270 NSEnumerator *keyEnum = nil;
271 NSString *key = nil;
272
273 if (_isPlayer) referenceSet = _playerKeys;
274 else if (_isStation) referenceSet = _stationKeys;
275 else referenceSet = _basicKeys;
276
277 for (keyEnum = [_info keyEnumerator]; (key = [keyEnum nextObject]); )
278 {
279 if (![referenceSet containsObject:key])
280 {
281 if ([_allKeys containsObject:key])
282 {
283 if (!_isTemplate)
284 {
285 // if it's a template, this key might apply to a descendant
286 // as happens in the core files
287 [self message:@"----- WARNING: key \"%@\" does not apply to this category of ship.", key];
288 }
289 }
290 else
291 {
292 [self message:@"----- WARNING: unknown key \"%@\".", key];
293 }
294 }
295 }
296}
297
298
299- (void)checkSchema
300{
301 [_schemaVerifier verifyPropertyList:_info named:_name];
302}
303
304
305- (void)checkModel
306{
307 id model = nil,
308 materials = nil,
309 shaders = nil;
310
311 model = [_info oo_stringForKey:@"model"];
312 materials = [_info oo_dictionaryForKey:@"materials"];
313 shaders = [_info oo_dictionaryForKey:@"shaders"];
314
315 if (model != nil)
316 {
317 if (![[[self verifier] modelVerifierStage] modelNamed:model
318 usedForEntry:_name
319 inFile:@"shipdata.plist"
320 withMaterials:materials
321 andShaders:shaders])
322 {
323 [self message:@"----- WARNING: model \"%@\" could not be found in %@ or in Oolite.", model, [[self verifier] oxpDisplayName]];
324 }
325 }
326 else
327 {
328 if ([_info oo_stringForKey:@"like_ship"] == nil)
329 {
330 [self message:@"***** ERROR: ship does not specify model or like_ship."];
331 }
332 }
333}
334
335
336// Convert a roles string to a set of role names, discarding probabilities.
337- (NSSet *)rolesFromString:(NSString *)string
338{
339 NSArray *parts = nil;
340 NSMutableSet *result = nil;
341 NSUInteger i, count;
342 NSString *role = nil;
343 NSRange parenRange;
344
345 if (string == nil) return [NSSet set];
346
347 parts = ScanTokensFromString(string);
348 count = [parts count];
349 if (count == 0) return [NSSet set];
350
351 result = [NSMutableSet setWithCapacity:count];
352 for (i = 0; i != count; ++i)
353 {
354 role = [parts objectAtIndex:i];
355 parenRange = [role rangeOfString:@"("];
356 if (parenRange.location != NSNotFound)
357 {
358 role = [role substringToIndex:parenRange.location];
359 }
360 [result addObject:role];
361 }
362
363 return result;
364}
365
366
367- (BOOL)verifier:(OOPListSchemaVerifier *)verifier
368withPropertyList:(id)rootPList
369 named:(NSString *)name
370 testProperty:(id)subPList
371 atPath:(NSArray *)keyPath
372 againstType:(NSString *)typeKey
373 error:(NSError **)outError
374{
375 [self verboseMessage:@"- Skipping verification for type %@ at %@.%@.", typeKey, _name, [OOPListSchemaVerifier descriptionForKeyPath:keyPath]];
376 return YES;
377}
378
379
380- (BOOL)verifier:(OOPListSchemaVerifier *)verifier
381withPropertyList:(id)rootPList
382 named:(NSString *)name
383 failedForProperty:(id)subPList
384 withError:(NSError *)error
385 expectedType:(NSDictionary *)localSchema
386{
387 // FIXME: use fancy new error codes to provide useful error descriptions.
388 [self message:@"***** ERROR: verification of ship \"%@\" failed at \"%@\": %@", name, [error plistKeyPathDescription], [error localizedFailureReason]];
389 return YES;
390}
391
392@end
393
394#endif
static NSString *const kStageName
static NSString *const kStageName
void OOLogPushIndent(void)
Definition OOLogging.m:316
void OOLogPopIndent(void)
Definition OOLogging.m:340
void void void OOLogWithFunctionFileAndLineAndArguments(NSString *inMessageClass, const char *inFunction, const char *inFile, unsigned long inLine, NSString *inFormat, va_list inArguments) OO_TAKES_FORMAT_STRING(5
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
Definition OOLogging.m:144
#define OOLog(class, format,...)
Definition OOLogging.h:88
void OOLogIndent(void)
Definition OOLogging.m:366
unsigned count
return nil
NSMutableArray * ScanTokensFromString(NSString *values)
NSString * nameForReverseDependencyForVerifier:(OOOXPVerifier *verifier)
id plistNamed:inFolder:referencedFrom:checkBuiltIn:(NSString *file,[inFolder] NSString *folder,[referencedFrom] NSString *context,[checkBuiltIn] BOOL checkBuiltIn)
BOOL fileExists:inFolder:referencedFrom:checkBuiltIn:(NSString *file,[inFolder] NSString *folder,[referencedFrom] NSString *context,[checkBuiltIn] BOOL checkBuiltIn)
NSString * nameForReverseDependencyForVerifier:(OOOXPVerifier *verifier)
instancetype verifierWithSchema:(NSDictionary *schema)
NSString * descriptionForKeyPath:(NSArray *keyPath)
NSDictionary * dictionaryFromFilesNamed:inFolder:andMerge:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles)