Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOSystemDescriptionManager.m
Go to the documentation of this file.
1/*
2
3OOPlanetDescriptionManager.h
4
5Singleton class responsible for planet description data.
6
7Oolite
8Copyright (C) 2004-2013 Giles C Williams and contributors
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23MA 02110-1301, USA.
24
25*/
26
28#import "OOStringParsing.h"
30#import "OOTypes.h"
31#import "PlayerEntity.h"
32#import "Universe.h"
33#import "ResourceManager.h"
34
35// character sequence which can't be in property name, system key or layer
36// and is highly unlikely to be in a manifest identifier
37static NSString * const kOOScriptedChangeJoiner = @"~|~";
38
39// likely maximum number of planetinfo properties to be applied to a system
40// just for efficiency - no harm in exceeding it
41#define OO_LIKELY_PROPERTIES_PER_SYSTEM 50
42
43@interface OOSystemDescriptionManager (OOPrivate)
44- (void) setProperties:(NSDictionary *)properties inDescription:(OOSystemDescriptionEntry *)desc;
45- (NSDictionary *) calculatePropertiesForSystemKey:(NSString *)key;
46- (void) updateCacheEntry:(NSUInteger)i;
47- (void) updateCacheEntry:(NSUInteger)i forProperty:(NSString *)property;
48- (id) getProperty:(NSString *)property forSystemKey:(NSString *)key withUniversal:(BOOL)universal;
49/* some planetinfo properties have two ways to specify
50 * need to get the one with higher layer (if they're both at the same layer,
51 * go with property1) */
52- (id) getProperty:(NSString *)property1 orProperty:(NSString *)property2 forSystemKey:(NSString *)key withUniversal:(BOOL)universal;
53
54- (void) saveScriptedChangeToProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest;
55
56@end
57
58static NSString *kOOSystemLayerProperty = @"layer";
59
60@implementation OOSystemDescriptionManager
61
62- (id) init
63{
64 self = [super init];
65 if (self != nil)
66 {
67 universalProperties = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
68 interstellarSpace = [[OOSystemDescriptionEntry alloc] init];
69 // assume specific interstellar settings are rare
70 systemDescriptions = [[NSMutableDictionary alloc] initWithCapacity:OO_SYSTEM_CACHE_LENGTH+20];
71 for (NSUInteger i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
72 {
73 propertyCache[i] = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
74 // hub count of 24 is considerably higher than occurs in
75 // standard planetinfo
76 neighbourCache[i] = [[NSMutableArray alloc] initWithCapacity:24];
77 }
78 propertiesInUse = [[NSMutableSet alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
79 scriptedChanges = [[NSMutableDictionary alloc] initWithCapacity:64];
80 }
81 return self;
82}
83
84- (void) dealloc
85{
86 DESTROY(universalProperties);
87 DESTROY(interstellarSpace);
88 DESTROY(systemDescriptions);
89 for (NSUInteger i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
90 {
91 DESTROY(propertyCache[i]);
92 DESTROY(neighbourCache[i]);
93 }
94 DESTROY(propertiesInUse);
95 DESTROY(scriptedChanges);
96 [super dealloc];
97}
98
99
100- (void) buildRouteCache
101{
102 NSUInteger i,j,k,jIndex,kIndex;
103 // firstly, cache all coordinates
104 for (i=0;i<OO_SYSTEM_CACHE_LENGTH;i++)
105 {
106 coordinatesCache[i] = PointFromString([propertyCache[i] oo_stringForKey:@"coordinates"]);
107 }
108 // now for each system find its neighbours
109 for (i=0;i<OO_GALAXIES_AVAILABLE;i++)
110 {
111 // one galaxy at a time
112 for (j=0;j<OO_SYSTEMS_PER_GALAXY;j++)
113 {
114 jIndex = j+(i*OO_SYSTEMS_PER_GALAXY);
115 for (k=j+1;k<OO_SYSTEMS_PER_GALAXY;k++)
116 {
117 kIndex = k+(i*OO_SYSTEMS_PER_GALAXY);
118 if (distanceBetweenPlanetPositions(coordinatesCache[jIndex].x,coordinatesCache[jIndex].y,coordinatesCache[kIndex].x,coordinatesCache[kIndex].y) <= MAX_JUMP_RANGE)
119 {
120 // arrays are of system number only
121 [neighbourCache[jIndex] addObject:[NSNumber numberWithUnsignedInteger:k]];
122 [neighbourCache[kIndex] addObject:[NSNumber numberWithUnsignedInteger:j]];
123 }
124 }
125 }
126 }
127
128}
129
130
131- (void) setUniversalProperties:(NSDictionary *)properties
132{
133 [universalProperties addEntriesFromDictionary:properties];
134 [propertiesInUse addObjectsFromArray:[properties allKeys]];
135 for (NSUInteger i = 0; i<OO_SYSTEM_CACHE_LENGTH; i++)
136 {
137 [self updateCacheEntry:i];
138 }
139}
140
141
142- (void) setInterstellarProperties:(NSDictionary *)properties
143{
144 [self setProperties:properties inDescription:interstellarSpace];
145}
146
147
148- (void) setProperties:(NSDictionary *)properties forSystemKey:(NSString *)key
149{
150 OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
151 if (desc == nil)
152 {
153 // create it
154 desc = [[[OOSystemDescriptionEntry alloc] init] autorelease];
155 [systemDescriptions setObject:desc forKey:key];
156 }
157 [self setProperties:properties inDescription:desc];
158 [propertiesInUse addObjectsFromArray:[properties allKeys]];
159
160 NSArray *tokens = ScanTokensFromString(key);
161 if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
162 {
163 OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
164 OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
165 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
166 if (index >= OO_SYSTEM_CACHE_LENGTH)
167 {
168 OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
169 }
170 else
171 {
172 [self updateCacheEntry:index];
173 }
174 }
175}
176
177
178- (void) setProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest
179{
180 OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
181 if (desc == nil)
182 {
183 // create it
184 desc = [[[OOSystemDescriptionEntry alloc] init] autorelease];
185 [systemDescriptions setObject:desc forKey:key];
186 }
187 [desc setProperty:property forLayer:layer toValue:value];
188 [propertiesInUse addObject:property];
189
190 NSArray *tokens = ScanTokensFromString(key);
191 if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
192 {
193 [self saveScriptedChangeToProperty:property forSystemKey:key andLayer:layer toValue:value fromManifest:manifest];
194
195 OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
196 OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
197 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
198 if (index >= OO_SYSTEM_CACHE_LENGTH)
199 {
200 OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
201 }
202 else
203 {
204 [self updateCacheEntry:index forProperty:property];
205 }
206 }
207 // for interstellar updates, save but don't update cache
208 else if ([tokens count] == 4 && [tokens oo_unsignedIntegerAtIndex:1] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:2] < OO_SYSTEMS_PER_GALAXY && [tokens oo_unsignedIntegerAtIndex:3] < OO_SYSTEMS_PER_GALAXY)
209 {
210 [self saveScriptedChangeToProperty:property forSystemKey:key andLayer:layer toValue:value fromManifest:manifest];
211 }
212}
213
214
215- (void) saveScriptedChangeToProperty:(NSString *)property forSystemKey:(NSString *)key andLayer:(OOSystemLayer)layer toValue:(id)value fromManifest:(NSString *)manifest
216{
217 // if OXP doesn't have a manifest, cancel saving the change
218 if (manifest == nil)
219 {
220 return;
221 }
222// OOLog(@"saving change",@"%@ %@ %@ %d",manifest,key,property,layer);
223 NSArray *overrideKey = [NSArray arrayWithObjects:manifest,key,property,[[NSNumber numberWithInt:layer] stringValue],nil];
224 // Obj-C copes with NSArray keys to dictionaries fine, but the
225 // plist format doesn't, so they can't be saved.
226 NSString *overrideKeyStr = [overrideKey componentsJoinedByString:kOOScriptedChangeJoiner];
227 if (value != nil)
228 {
229 [scriptedChanges setObject:value forKey:overrideKeyStr];
230 }
231 else
232 {
233 [scriptedChanges removeObjectForKey:overrideKeyStr];
234 }
235}
236
237
238- (void) importScriptedChanges:(NSDictionary *)scripted
239{
240 NSArray *key = nil;
241 NSString *keyStr = nil;
242 NSString *manifest = nil;
243 foreachkey(keyStr, scripted)
244 {
245 key = [keyStr componentsSeparatedByString:kOOScriptedChangeJoiner];
246 if ([key count] == 4)
247 {
248 manifest = [key oo_stringAtIndex:0];
249 if ([ResourceManager manifestForIdentifier:manifest] != nil)
250 {
251// OOLog(@"importing",@"%@ -> %@",keyStr,[scripted objectForKey:keyStr]);
252 [self setProperty:[key oo_stringAtIndex:2]
253 forSystemKey:[key oo_stringAtIndex:1]
254 andLayer:[key oo_intAtIndex:3]
255 toValue:[scripted objectForKey:keyStr]
256 fromManifest:manifest];
257 // and doing this set stores it into the manager's copy
258 // of scripted changes
259 // this means in theory we could import more than one
260 }
261 // else OXP not installed, do not load
262 }
263 else
264 {
265 OOLog(@"systemManager.import",@"Key '%@' has unexpected format - skipping",keyStr);
266 }
267 }
268
269}
270
271
272// import of the old local_planetinfo_overrides dictionary
273- (void) importLegacyScriptedChanges:(NSDictionary *)scripted
274{
275 NSString *systemKey = nil;
276 NSString *propertyKey = nil;
277 NSString *defaultManifest = @"org.oolite.oolite";
278
279 foreachkey(systemKey,scripted)
280 {
281 NSDictionary *legacyChanges = [scripted oo_dictionaryForKey:systemKey];
282 if ([legacyChanges objectForKey:@"sun_gone_nova"] != nil)
283 {
284 // then this is a change to import even if we don't know
285 // if the OXP is still installed
286 foreachkey (propertyKey, legacyChanges)
287 {
288 [self setProperty:propertyKey
289 forSystemKey:systemKey
290 andLayer:OO_LAYER_OXP_DYNAMIC
291 toValue:[legacyChanges objectForKey:propertyKey]
292 fromManifest:defaultManifest];
293 }
294 /* Fix for older savegames not having a larger sun radius
295 * property set from the Nova mission. */
296 id sr = [self getProperty:@"sun_radius" forSystemKey:systemKey];
297 float sr_num = [sr floatValue];
298 if (sr_num < 600000) {
299 // fix sun radius values
300 [self setProperty:@"sun_radius"
301 forSystemKey:systemKey
302 andLayer:OO_LAYER_OXP_DYNAMIC
303 toValue:[NSNumber numberWithFloat:sr_num+600000.0f]
304 fromManifest:defaultManifest];
305 }
306 }
307 }
308
309}
310
311
312- (NSDictionary *) exportScriptedChanges
313{
314 return [[scriptedChanges copy] autorelease];
315}
316
317
318- (NSDictionary *) getPropertiesForCurrentSystem
319{
320 OOSystemID s = [UNIVERSE currentSystemID];
321 if (s >= 0)
322 {
323 NSUInteger index = ([PLAYER galaxyNumber] * OO_SYSTEMS_PER_GALAXY) + s;
324 if (index >= OO_SYSTEM_CACHE_LENGTH)
325 {
326 OOLog(@"system.description.error",@"'%lu' is an invalid system index for the current system. This is an internal error. Please report it.",index);
327 return [NSDictionary dictionary];
328 }
329 return propertyCache[index];
330 }
331 else
332 {
333 OOLog(@"system.description.error", @"%@", @"getPropertiesForCurrentSystem called while player in interstellar space. This is an internal error. Please report it.");
334 // this shouldn't be called for interstellar space
335 return [NSDictionary dictionary];
336 }
337}
338
339
340- (NSDictionary *) getPropertiesForSystemKey:(NSString *)key
341{
342 NSArray *tokens = ScanTokensFromString(key);
343 if ([tokens count] == 2 && [tokens oo_unsignedIntegerAtIndex:0] < OO_GALAXIES_AVAILABLE && [tokens oo_unsignedIntegerAtIndex:1] < OO_SYSTEMS_PER_GALAXY)
344 {
345 OOGalaxyID g = [tokens oo_unsignedIntegerAtIndex:0];
346 OOSystemID s = [tokens oo_unsignedIntegerAtIndex:1];
347 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
348 if (index >= OO_SYSTEM_CACHE_LENGTH)
349 {
350 OOLog(@"system.description.error",@"'%@' is an invalid system key. This is an internal error. Please report it.",key);
351 return [NSDictionary dictionary];
352 }
353 return propertyCache[index];
354 }
355 // interstellar spaces aren't cached
356 return [self calculatePropertiesForSystemKey:key];
357}
358
359
360- (NSDictionary *) getPropertiesForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
361{
362 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
363 if (index >= OO_SYSTEM_CACHE_LENGTH)
364 {
365 OOLog(@"system.description.error",@"'%u, %u' is an invalid system. This is an internal error. Please report it.",g,s);
366 return [NSDictionary dictionary];
367 }
368 return propertyCache[index];
369}
370
371
372- (id) getProperty:(NSString *)property forSystemKey:(NSString *)key
373{
374 return [self getProperty:property forSystemKey:key withUniversal:YES];
375}
376
377- (id) getProperty:(NSString *)property forSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
378{
379 if (s < 0)
380 {
381 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
382 return nil;
383 }
384 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
385 if (index >= OO_SYSTEM_CACHE_LENGTH)
386 {
387 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
388 return nil;
389 }
390 return [propertyCache[index] objectForKey:property];
391}
392
393
394- (id) getProperty:(NSString *)property forSystemKey:(NSString *)key withUniversal:(BOOL)universal
395{
397 if (EXPECT_NOT([key isEqualToString:@"interstellar"]))
398 {
399 desc = interstellarSpace;
400 }
401 else
402 {
403 desc = [systemDescriptions objectForKey:key];
404 }
405 if (desc == nil)
406 {
407 return nil;
408 }
409 id result = nil;
410 result = [desc getProperty:property forLayer:OO_LAYER_OXP_PRIORITY];
411 if (result == nil)
412 {
413 result = [desc getProperty:property forLayer:OO_LAYER_OXP_DYNAMIC];
414 }
415 if (result == nil)
416 {
417 result = [desc getProperty:property forLayer:OO_LAYER_OXP_STATIC];
418 }
419 if (result == nil && universal)
420 {
421 result = [universalProperties objectForKey:property];
422 }
423 if (result == nil)
424 {
425 result = [desc getProperty:property forLayer:OO_LAYER_CORE];
426 }
427 return result;
428}
429
430
431- (id) getProperty:(NSString *)property1 orProperty:(NSString *)property2 forSystemKey:(NSString *)key withUniversal:(BOOL)universal
432{
433 OOSystemDescriptionEntry *desc = [systemDescriptions objectForKey:key];
434 if (desc == nil)
435 {
436 return nil;
437 }
438 id result = nil;
439 result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_PRIORITY];
440 if (result == nil)
441 {
442 result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_PRIORITY];
443 }
444 if (result == nil)
445 {
446 result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_DYNAMIC];
447 }
448 if (result == nil)
449 {
450 result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_DYNAMIC];
451 }
452 if (result == nil)
453 {
454 result = [desc getProperty:property1 forLayer:OO_LAYER_OXP_STATIC];
455 }
456 if (result == nil)
457 {
458 result = [desc getProperty:property2 forLayer:OO_LAYER_OXP_STATIC];
459 }
460 if (universal)
461 {
462 if (result == nil)
463 {
464 result = [universalProperties objectForKey:property1];
465 }
466 if (result == nil)
467 {
468 result = [universalProperties objectForKey:property2];
469 }
470 }
471 if (result == nil)
472 {
473 result = [desc getProperty:property1 forLayer:OO_LAYER_CORE];
474 }
475 if (result == nil)
476 {
477 result = [desc getProperty:property2 forLayer:OO_LAYER_CORE];
478 }
479 return result;
480}
481
482
483- (void) setProperties:(NSDictionary *)properties inDescription:(OOSystemDescriptionEntry *)desc
484{
485 OOSystemLayer layer = [properties oo_unsignedIntForKey:kOOSystemLayerProperty defaultValue:OO_LAYER_OXP_STATIC];
486 if (layer > OO_LAYER_OXP_PRIORITY)
487 {
488 OOLog(@"system.description.error",@"Layer %u is not a valid layer number in system information.",layer);
489 layer = OO_LAYER_OXP_PRIORITY;
490 }
491 NSString *key = nil;
492 foreachkey(key, properties)
493 {
494 if (![key isEqualToString:kOOSystemLayerProperty])
495 {
496 [propertiesInUse addObject:key];
497 [desc setProperty:key forLayer:layer toValue:[properties objectForKey:key]];
498 }
499 }
500}
501
502
503- (NSDictionary *) calculatePropertiesForSystemKey:(NSString *)key
504{
505 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
506 NSString *property = nil;
507 id val = nil;
508 BOOL interstellar = [key hasPrefix:@"interstellar:"];
509 foreach (property, propertiesInUse)
510 {
511 // don't use universal properties on interstellar specific regions
512 val = [self getProperty:property forSystemKey:key withUniversal:!interstellar];
513
514 if (val != nil)
515 {
516 [dict setObject:val forKey:property];
517 }
518 else if (interstellar)
519 {
520 // interstellar is always overridden by specific regions
521 // universal properties for interstellar get picked up here
522 val = [self getProperty:property forSystemKey:@"interstellar"];
523 if (val != nil)
524 {
525 [dict setObject:val forKey:property];
526 }
527 }
528 }
529 return dict;
530}
531
532
533- (void) updateCacheEntry:(NSUInteger)i
534{
535 NSAssert(i < OO_SYSTEM_CACHE_LENGTH,@"Invalid cache entry number");
536 NSString *key = [NSString stringWithFormat:@"%lu %lu",i/OO_SYSTEMS_PER_GALAXY,i%OO_SYSTEMS_PER_GALAXY];
537 NSDictionary *current = [self calculatePropertiesForSystemKey:key];
538
539 [propertyCache[i] removeAllObjects];
540 [propertyCache[i] addEntriesFromDictionary:current];
541}
542
543
544- (void) updateCacheEntry:(NSUInteger)i forProperty:(NSString *)property
545{
546 NSAssert(i < OO_SYSTEM_CACHE_LENGTH,@"Invalid cache entry number");
547 NSString *key = [NSString stringWithFormat:@"%lu %lu",i/OO_SYSTEMS_PER_GALAXY,i%OO_SYSTEMS_PER_GALAXY];
548 id current = [self getProperty:property forSystemKey:key];
549 if (current == nil)
550 {
551 [propertyCache[i] removeObjectForKey:property];
552 }
553 else
554 {
555 [propertyCache[i] setObject:current forKey:property];
556 }
557}
558
559
560- (NSPoint) getCoordinatesForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
561{
562 if (s < 0)
563 {
564 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
565 return (NSPoint){0,0};
566 }
567 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
568 if (index >= OO_SYSTEM_CACHE_LENGTH)
569 {
570 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
571 return (NSPoint){0,0};
572 }
573 return coordinatesCache[index];
574}
575
576
577- (NSArray *) getNeighbourIDsForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
578{
579 if (s < 0)
580 {
581 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
582 return nil;
583 }
584 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
585 if (index >= OO_SYSTEM_CACHE_LENGTH)
586 {
587 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
588 return nil;
589 }
590
591 return neighbourCache[index];
592}
593
594
595- (Random_Seed) getRandomSeedForCurrentSystem
596{
597 if ([UNIVERSE currentSystemID] < 0)
598 {
599 return kNilRandomSeed;
600 }
601 else
602 {
603 OOSystemID s = [UNIVERSE currentSystemID];
604 NSUInteger index = ([PLAYER galaxyNumber] * OO_SYSTEMS_PER_GALAXY) + s;
605 if (index >= OO_SYSTEM_CACHE_LENGTH)
606 {
607 OOLog(@"system.description.error",@"'%lu' is an invalid system index for the current system. This is an internal error. Please report it.",(unsigned long)index);
608 return kNilRandomSeed;
609 }
610 return RandomSeedFromString([propertyCache[index] oo_stringForKey:@"random_seed"]);
611 }
612}
613
614
615- (Random_Seed) getRandomSeedForSystem:(OOSystemID)s inGalaxy:(OOGalaxyID)g
616{
617 if (s < 0)
618 {
619 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
620 return kNilRandomSeed;
621 }
622 NSUInteger index = (g * OO_SYSTEMS_PER_GALAXY) + s;
623 if (index >= OO_SYSTEM_CACHE_LENGTH)
624 {
625 OOLog(@"system.description.error",@"'%d %d' is an invalid system key. This is an internal error. Please report it.",g,s);
626 return kNilRandomSeed;
627 }
628 return RandomSeedFromString([propertyCache[index] oo_stringForKey:@"random_seed"]);
629}
630
631
632@end
633
634
635@interface OOSystemDescriptionEntry (OOPrivate)
636- (id) validateProperty:(NSString *)property withValue:(id)value;
637@end
638
639@implementation OOSystemDescriptionEntry
640
641- (id) init
642{
643 self = [super init];
644 if (self != nil)
645 {
646 for (NSUInteger i=0;i<OO_SYSTEM_LAYERS;i++)
647 {
648 layers[i] = [[NSMutableDictionary alloc] initWithCapacity:OO_LIKELY_PROPERTIES_PER_SYSTEM];
649 }
650 }
651 return self;
652}
653
654
655- (void) dealloc
656{
657 for (NSUInteger i=0;i<OO_SYSTEM_LAYERS;i++)
658 {
659 DESTROY(layers[i]);
660 }
661 [super dealloc];
662}
663
664
665- (void) setProperty:(NSString *)property forLayer:(OOSystemLayer)layer toValue:(id)value
666{
667 if (value == nil)
668 {
669 [layers[layer] removeObjectForKey:property];
670 }
671 else
672 {
673 // validate type of object for certain properties
674 value = [self validateProperty:property withValue:value];
675 // if it's nil now, validation failed and could not be recovered
676 // so don't actually set anything
677 if (value != nil)
678 {
679 [layers[layer] setObject:value forKey:property];
680 }
681 }
682}
683
684
685- (id) getProperty:(NSString *)property forLayer:(OOSystemLayer)layer
686{
687 return [layers[layer] objectForKey:property];
688}
689
690
691/* Mostly the rest of the game gets a system dictionary from
692 * [UNIVERSE currentSystemData] or similar, which means that it uses
693 * safe methods like oo_stringForKey: - a few things use a direct call
694 * to getProperty for various reasons, so need some type validation
695 * here instead. */
696- (id) validateProperty:(NSString *)property withValue:(id)value
697{
698 if ([property isEqualToString:@"coordinates"])
699 {
700 // must be a string with two numbers in it
701 // TODO: convert two element arrays
702 if (![value isKindOfClass:[NSString class]])
703 {
704 OOLog(@"system.description.error",@"'%@' is not a valid format for coordinates",value);
705 return nil;
706 }
707 NSArray *tokens = ScanTokensFromString((NSString *)value);
708 if ([tokens count] != 2)
709 {
710 OOLog(@"system.description.error",@"'%@' is not a valid format for coordinates (must have exactly two numbers)",value);
711 return nil;
712 }
713 }
714 else if ([property isEqualToString:@"radius"] || [property isEqualToString:@"government"])
715 {
716 // read in a context which expects a string, but it's a string representation of a number
717 if (![value isKindOfClass:[NSString class]])
718 {
719 if ([value isKindOfClass:[NSNumber class]])
720 {
721 return [value stringValue];
722 }
723 else
724 {
725 OOLog(@"system.description.error",@"'%@' is not a valid value for '%@' (string required)",value,property);
726 return nil;
727 }
728 }
729 }
730 else if ([property isEqualToString:@"inhabitant"] || [property isEqualToString:@"inhabitants"] || [property isEqualToString:@"name"] )
731 {
732 // read in a context which expects a string
733 if (![value isKindOfClass:[NSString class]])
734 {
735 OOLog(@"system.description.error",@"'%@' is not a valid value for '%@' (string required)",value,property);
736 return nil;
737 }
738 }
739
740 return value;
741}
742
743@end
#define DESTROY(x)
Definition OOCocoa.h:77
#define foreachkey(VAR, DICT)
Definition OOCocoa.h:366
#define EXPECT_NOT(x)
#define OOLog(class, format,...)
Definition OOLogging.h:88
unsigned count
return nil
float y
float x
NSPoint PointFromString(NSString *xyString)
NSMutableArray * ScanTokensFromString(NSString *values)
Random_Seed RandomSeedFromString(NSString *abcdefString)
#define OO_GALAXIES_AVAILABLE
#define OO_SYSTEM_CACHE_LENGTH
#define OO_SYSTEM_LAYERS
#define OO_SYSTEMS_PER_GALAXY
static NSString * kOOSystemLayerProperty
static NSString *const kOOScriptedChangeJoiner
int16_t OOSystemID
Definition OOTypes.h:211
uint8_t OOGalaxyID
Definition OOTypes.h:210
#define MAX_JUMP_RANGE
Definition ShipEntity.h:107
#define UNIVERSE
Definition Universe.h:833
id getProperty:forLayer:(NSString *property,[forLayer] OOSystemLayer layer)
void setProperty:forLayer:toValue:(NSString *property,[forLayer] OOSystemLayer layer,[toValue] id value)
const Random_Seed kNilRandomSeed
OOINLINE double distanceBetweenPlanetPositions(int x1, int y1, int x2, int y2) INLINE_CONST_FUNC