cfg.py (24772B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\script\sys\cfg.py 2 import sys 3 import types 4 import copy 5 import blue 6 import carbon.common.script.sys.basesession as base 7 import carbon.common.script.sys.crowset as crowset 8 import carbon.common.script.sys.service as service 9 import carbon.common.script.util.format as formatUtil 10 import fsdBuiltData.common.character as fsdchar 11 import localization 12 import log 13 import telemetry 14 import uthread 15 import utillib 16 from carbon.common.script.sys.row import Row as utilRow 17 from eve.common.script.sys import idCheckers 18 from carbon.common.lib import const 19 from eveprefs import boot 20 from utillib import strx 21 LogError = None 22 LogInfo = None 23 LogWarn = None 24 LogNotice = None 25 26 class DataConfig(service.CoreService): 27 __guid__ = 'svc.dataconfig' 28 __startupdependencies__ = [] 29 __servicename__ = 'dataconfig' 30 __displayname__ = 'Data and Configuration Service' 31 __exportedcalls__ = {'GetStartupData': [service.ROLE_SERVICE]} 32 __notifyevents__ = ['OnCfgDataChanged', 'ProcessLoginProgressDone'] 33 34 def __init__(self): 35 service.CoreService.__init__(self) 36 self.cfg = None 37 38 def _CreateConfig(self): 39 return Config() 40 41 def Run(self, memStream = None): 42 global LogWarn 43 global LogError 44 global LogInfo 45 global LogNotice 46 service.CoreService.Run(self, memStream) 47 self.state = service.SERVICE_START_PENDING 48 import __builtin__ 49 if not hasattr(__builtin__, 'cfg'): 50 __builtin__.cfg = self._CreateConfig() 51 LogError = cfg.LogError = self.LogError 52 LogInfo = cfg.LogInfo = self.LogInfo 53 LogWarn = cfg.LogWarn = self.LogWarn 54 LogNotice = cfg.LogNotice = self.LogNotice 55 cfg.GetStartupData() 56 self.state = service.SERVICE_RUNNING 57 58 def Stop(self, memStream = None): 59 tmp = cfg 60 import __builtin__ 61 delattr(__builtin__, 'const') 62 delattr(__builtin__, 'cfg') 63 tmp.Release() 64 service.CoreService.Stop(self) 65 66 def ProcessLoginProgressDone(self): 67 self.LogInfo('Login progress done, loading cfg data') 68 cfg.LoadCfgData() 69 70 def OnCfgDataChanged(self, what, data): 71 uthread.new(cfg.OnCfgDataChanged, what, data) 72 73 def DeleteRowTheHardWay(self, rs, keyIDs, keyCols): 74 keyID, keyID2, keyID3 = keyIDs 75 keyCol1, keyCol2, keyCol3 = keyCols 76 delRow = None 77 if keyCol3: 78 for r in rs: 79 if r[keyCol1] == keyID and r[keyCol2] == keyID2 and r[keyCol3] == keyID3: 80 delRow = r 81 break 82 83 elif keyCol2: 84 for r in rs: 85 if r[keyCol1] == keyID and r[keyCol2] == keyID2: 86 delRow = r 87 break 88 89 else: 90 for r in rs: 91 if r[keyCol1] == keyID: 92 delRow = r 93 break 94 95 if delRow: 96 rs.remove(delRow) 97 98 def GetStartupData(self): 99 self.LogInfo('Getting startup data') 100 cfg.GetStartupData() 101 102 103 class ProxyServiceBroker(): 104 105 def ConnectToService(self, name): 106 sess = base.GetServiceSession('cfg') 107 return sess.ConnectToRemoteService(name) 108 109 110 class Config(): 111 __guid__ = 'util.config' 112 113 def __init__(self): 114 self.servicebroker = None 115 self.client = 0 116 self.server = 0 117 self.totalLogonSteps = 10 118 self.fmtMapping = {} 119 self.fmtMapping[const.UE_DATETIME] = lambda value, value2: formatUtil.FmtDate(value) 120 self.fmtMapping[const.UE_DATE] = lambda value, value2: formatUtil.FmtDate(value, 'ln') 121 self.fmtMapping[const.UE_TIME] = lambda value, value2: formatUtil.FmtDate(value, 'nl') 122 self.fmtMapping[const.UE_TIMESHRT] = lambda value, value2: formatUtil.FmtDate(value, 'ns') 123 self.fmtMapping[const.UE_MSGARGS] = lambda value, value2: cfg.GetMessage(value[0], value[1]).text 124 self.fmtMapping[const.UE_LOC] = lambda value, value2: self._GetLocalization(value, value2) 125 self.fmtMapping[const.UE_MESSAGEID] = lambda value, value2: self._GetLocalizationByID(value, value2) 126 self.fmtMapping[const.UE_LIST] = lambda value, value2: self._GetList(value, value2) 127 128 def UpdateCfgData(self, data, object, guid): 129 if guid == Recordset.__guid__: 130 keyidx = object.keyidx or object.header.index(object.keycolumn) 131 object.data[data[keyidx]] = data 132 elif guid == crowset.CFilterRowset.__guid__: 133 keyidx = object.columnName 134 index = data[keyidx] 135 if index in object: 136 object[data[keyidx]].append(data) 137 else: 138 object[data[keyidx]] = [data] 139 else: 140 raise RuntimeError('Call to OnCfgDataChanged unsupported container type.') 141 142 def OnCfgDataChanged(self, what, data): 143 if not hasattr(self, what): 144 raise RuntimeError('Call to OnCfgDataChanged with invalid container reference.') 145 object = getattr(self, what) 146 if not object: 147 raise RuntimeError('Call to OnCfgDataChanged failed to get a valid container.') 148 if type(object) == types.DictType: 149 object[data[0]] = data[1] 150 return 151 if not hasattr(object, '__guid__'): 152 raise RuntimeError('Call to OnCfgDataChanged for unknown container type.') 153 guid = getattr(object, '__guid__') 154 if not guid: 155 raise RuntimeError('Call to OnCfgDataChanged failed to get container type.') 156 self.UpdateCfgData(data, object, guid) 157 sm.ScatterEvent('OnPostCfgDataChanged', what, data) 158 159 def Release(self): 160 self.messages = None 161 self.LogError = None 162 163 def GetConfigSvc(self): 164 if not self.servicebroker: 165 sess = base.GetServiceSession('cfg') 166 if 'config' in sm.services or boot.role == 'server': 167 config = sess.ConnectToService('config') 168 self.servicebroker = sess 169 self.server = 1 170 elif boot.role == 'proxy': 171 self.servicebroker = ProxyServiceBroker() 172 self.client = 1 173 else: 174 self.servicebroker = sess.ConnectToService('connection') 175 self.client = 1 176 return self.servicebroker.ConnectToService('config') 177 178 def GetSvc(self, serviceid): 179 if not self.servicebroker: 180 sess = base.GetServiceSession('cfg') 181 self.servicebroker = sess 182 self.server = 1 183 return self.servicebroker.ConnectToService(serviceid) 184 185 def GetStartupData(self): 186 if boot.role != 'server': 187 return 188 self.AppGetStartupData() 189 190 def AppGetStartupData(self): 191 pass 192 193 def ConvertData(self, src, dst): 194 dst.data.clear() 195 dst.header = src.columns 196 keycol = src.columns.index(dst.keycolumn) 197 for i in src: 198 dst.data[i[keycol]] = i 199 200 @telemetry.ZONE_METHOD 201 def LoadCfgData(self): 202 self.LogInfo('LoadCfgData') 203 self.paperdollModifierLocations = fsdchar.GetModifierLocationRows() 204 self.paperdollResources = fsdchar.GetResourceRows() 205 self.paperdollSculptingLocations = fsdchar.GetSculptingLocationRows() 206 self.paperdollColors = fsdchar.GetColorLocationRows() 207 self.paperdollColorNames = fsdchar.GetColorNameRows() 208 self.paperdollPortraitResources = fsdchar.GetPortraitResourceRows() 209 210 def GetMessage(self, key, dict = None, onNotFound = 'return', onDictMissing = 'error'): 211 if key not in self.messages: 212 if onNotFound == 'return': 213 return self.GetMessage('ErrMessageNotFound', {'msgid': key, 214 'args': dict}) 215 if onNotFound == 'raise': 216 raise RuntimeError('ErrMessageNotFound', {'msgid': key, 217 'args': dict}) 218 else: 219 if onNotFound == 'pass': 220 return 221 raise RuntimeError('GetMessage: WTF', onNotFound) 222 msg = self.messages[key] 223 text, title, suppress = msg.messageText, None, 0 224 if dict == -1: 225 pass 226 elif dict is not None: 227 try: 228 if dict is not None and dict != -1: 229 dict = self.__prepdict(dict) 230 if text is not None: 231 text = text % dict 232 except KeyError as e: 233 if onNotFound == 'raise': 234 raise 235 sys.exc_clear() 236 return self.GetMessage('ErrMessageKeyError', {'msgid': key, 237 'text': msg.messageText, 238 'args': dict, 239 'err': strx(e)}) 240 241 elif onDictMissing == 'error': 242 if text and text.find('%(') != -1: 243 if onNotFound == 'raise': 244 raise RuntimeError('ErrMessageDictMissing', {'msgid': key, 245 'text': msg.messageText}) 246 return self.GetMessage('ErrMessageDictMissing', {'msgid': key, 247 'text': msg.messageText}) 248 if text is not None: 249 ix = text.find('::') 250 if ix != -1: 251 title, text = text.split('::') 252 suppress = 0 253 else: 254 ix = text.find(':s:') 255 if ix != -1: 256 title, text = text.split(':s:') 257 suppress = 1 258 else: 259 text, title, suppress = (None, None, 0) 260 return utillib.KeyVal(text=text, title=title, type=msg.dialogType, audio=msg.urlAudio, icon=msg.urlIcon, suppress=suppress) 261 262 def _GetLocalization(self, key, keyArgs = None): 263 if boot.role == 'server': 264 if keyArgs is None: 265 return key 266 else: 267 return key + ' - ' + str(keyArgs) 268 else: 269 if keyArgs is None: 270 return localization.GetByLabel(key) 271 for k, v in keyArgs.iteritems(): 272 if type(v) != types.TupleType: 273 continue 274 value2 = None 275 if len(v) >= 3: 276 value2 = v[2] 277 keyArgs[k] = self.FormatConvert(v[0], v[1], value2) 278 279 return localization.GetByLabel(key, **keyArgs) 280 281 def _GetLocalizationByID(self, key, keyArgs = None): 282 if boot.role == 'server': 283 if keyArgs is None: 284 return 'MessageID: ' + str(key) 285 else: 286 return 'MessageID: ' + str(key) + ' - ' + str(keyArgs) 287 else: 288 if keyArgs is None: 289 return localization.GetByMessageID(key) 290 for k, v in keyArgs.iteritems(): 291 if type(v) != types.TupleType: 292 continue 293 value2 = None 294 if len(v) >= 3: 295 value2 = v[2] 296 keyArgs[k] = self.FormatConvert(v[0], v[1], value2) 297 298 return localization.GetByMessageID(key, **keyArgs) 299 300 def _GetList(self, fmtList, separator = None): 301 results = [] 302 for each in fmtList: 303 value2 = None 304 if len(each) >= 3: 305 value2 = each[2] 306 results.append(self.FormatConvert(each[0], each[1], value2)) 307 308 if separator is not None: 309 return separator.join(results) 310 else: 311 return localization.formatters.FormatGenericList(results) 312 313 def __prepdict(self, dict): 314 dict = copy.deepcopy(dict) 315 for k, v in dict.iteritems(): 316 if type(v) != types.TupleType: 317 continue 318 value2 = None 319 if len(v) >= 3: 320 value2 = v[2] 321 dict[k] = self.FormatConvert(v[0], v[1], value2) 322 323 return dict 324 325 __numberstrings__ = {1: 'one', 326 2: 'two', 327 3: 'three', 328 4: 'four', 329 5: 'five', 330 6: 'six', 331 7: 'seven', 332 8: 'eight', 333 9: 'nine', 334 10: 'ten', 335 11: 'eleven', 336 12: 'twelve', 337 13: 'thirteen', 338 14: 'fourteen', 339 15: 'fifteen', 340 16: 'sixteen', 341 17: 'seventeen', 342 18: 'eighteen', 343 19: 'nineteen', 344 20: 'twenty', 345 30: 'thirty', 346 40: 'forty', 347 50: 'fifty', 348 60: 'sixty', 349 70: 'seventy', 350 80: 'eighty', 351 90: 'ninety'} 352 for each in range(19, 0, -1): 353 __numberstrings__[each * 100] = __numberstrings__[each] + ' hundred' 354 __numberstrings__[each * 1000] = __numberstrings__[each] + ' thousand' 355 __numberstrings__[each * 100000] = __numberstrings__[each] + ' hundred thousand' 356 __numberstrings__[each * 1000000] = __numberstrings__[each] + ' million' 357 __numberstrings__[each * 1000000000] = __numberstrings__[each] + ' billion' 358 359 def FormatConvert(self, formatType, value, value2 = None): 360 if type(value) == types.TupleType: 361 value2 = None 362 if len(value) >= 3: 363 value2 = value[2] 364 value = self.FormatConvert(value[0], value[1], value2) 365 if formatType in self.fmtMapping: 366 return self.fmtMapping[formatType](value, value2) 367 else: 368 return 'INVALID FORMAT TYPE %s, value is %s' % (formatType, value) 369 370 def Format(self, key, dict = None): 371 msg = self.GetMessage(key, dict, onNotFound='raise') 372 if msg.title: 373 return '%s - %s' % (msg.title, msg.text) 374 else: 375 return msg.text 376 377 378 class Recordset(): 379 __guid__ = 'cfg.Recordset' 380 381 def __init__(self, rowclass, keycolumn, dbfetcher = None, dbMultiFetcher = None, clientLocalFetcher = None): 382 self.rowclass = rowclass 383 self.dbfetcher = dbfetcher 384 self.dbMultiFetcher = dbMultiFetcher 385 if boot.role == 'client': 386 self.clientLocalFetcher = clientLocalFetcher 387 else: 388 self.clientLocalFetcher = None 389 self.header = [] 390 self.data = {} 391 self.keyidx = None 392 self.keycolumn = keycolumn 393 self.locks = {} 394 self.waitingKeys = set() 395 self.knownLuzers = set([None]) 396 self.singlePrimeCount = 0 397 self.singlePrimeTimestamp = 0 398 if isinstance(self.dbfetcher, (list, tuple)): 399 header, data = self.dbfetcher 400 self.PopulateDataset(header, data) 401 402 def PopulateDataset(self, header, data): 403 self.header = header 404 self.keyidx = self.header.index(self.keycolumn) 405 self.__PopulateDataset(data) 406 407 def GetKeyColumn(self): 408 return self.keycolumn 409 410 def GetLine(self, key): 411 return self.data[key] 412 413 def __len__(self): 414 return len(self.data) 415 416 def __str__(self): 417 return self.__repr__() 418 419 def __repr__(self): 420 try: 421 ret = '<Instance of Recordset.%s>\r\n' % (self.rowclass.__name__,) 422 ret = ret + 'Key column: %s' % (self.keycolumn,) 423 if len(self.header): 424 ret = ret + ', Cache entries: %d\r\n' % (len(self),) 425 ret = ret + 'Field names: ' 426 for i in self.header: 427 ret = ret + i + ', ' 428 429 ret = ret[:-2] 430 else: 431 ret = ret + ', No cache entries.' 432 return ret 433 except: 434 sys.exc_clear() 435 return '<Instance of Recordset containing crappy data>' 436 437 def __LoadData(self, key): 438 if self.dbfetcher is None: 439 return 440 if type(self.dbfetcher) == types.StringType: 441 conf = cfg.GetConfigSvc() 442 fetch = getattr(conf, self.dbfetcher) 443 fk = fetch(key) 444 if isinstance(fk, Exception): 445 raise fk 446 if type(fk) == types.TupleType: 447 self.header, data = fk 448 elif hasattr(fk, '__columns__'): 449 self.header = fk.__columns__ 450 data = [list(fk)] 451 elif hasattr(fk, 'columns'): 452 self.header = fk.columns 453 data = fk 454 else: 455 raise RuntimeError('_LoadData called with unsupported data type', fk.__class__) 456 elif type(self.dbfetcher) in [types.ListType, types.TupleType]: 457 self.header, data = self.dbfetcher 458 else: 459 fetch = self.dbfetcher 460 self.header, data = fetch(key) 461 if self.keyidx is None: 462 self.keyidx = self.header.index(self.keycolumn) 463 self.__PopulateDataset(data, key, None) 464 465 def Hint(self, key, value): 466 if key: 467 if key in self.data and idCheckers.IsLocalIdentity(key): 468 return 469 if len(value) < len(self.header): 470 log.LogError('Recordset.Hint is receiving a value that is less than the header length, key=', key, '| classType=', self.rowclass, '| value=', value) 471 if len(self.header) == len(value): 472 self.data[key] = value 473 else: 474 self.data[key] = value[:len(self.header)] 475 elif hasattr(value, 'columns'): 476 self.__PopulateDataset(value, None, list) 477 else: 478 raise RuntimeError('Hint called with unsupported data type', value.__class__) 479 480 def Prime(self, keys, fuxorcheck = 1): 481 if fuxorcheck: 482 i = 0 483 for each in keys: 484 if each not in self.data: 485 i += 1 486 487 if i >= 50: 488 flag = [log.LGINFO, log.LGWARN][i >= 100] 489 log.general.Log('%s - Prime() called for %s items from %s' % (self.GetFetcherString(), i, log.WhoCalledMe()), flag) 490 if boot.role == 'client' and i == 1: 491 NUM_SEC = 2 492 s = int(blue.os.GetWallclockTime() / (NUM_SEC * const.SEC)) 493 if s > self.singlePrimeTimestamp + NUM_SEC: 494 self.singlePrimeCount = 0 495 self.singlePrimeTimestamp = s 496 self.singlePrimeCount += 1 497 if self.singlePrimeCount < 100: 498 tick = 10 499 flag = log.LGWARN 500 else: 501 tick = 50 502 flag = log.LGERR 503 if self.singlePrimeCount >= tick and self.singlePrimeCount % tick == 0: 504 log.general.Log('%s - Prime() called for %s single items. Last caller was %s. Caller might want to consider using Prime()' % (self.GetFetcherString(), self.singlePrimeCount, log.WhoCalledMe()), flag) 505 if keys: 506 self.waitingKeys.update(keys) 507 uthread.Lock(self, 0) 508 try: 509 self._Prime() 510 finally: 511 uthread.UnLock(self, 0) 512 513 def _Prime(self): 514 if self.dbMultiFetcher is None: 515 return 516 if not self.waitingKeys: 517 return 518 keysToGet = self.waitingKeys 519 localKeysToGet = set() 520 keysIAlreadyHave = set() 521 for key in keysToGet: 522 if key in self.data: 523 keysIAlreadyHave.add(key) 524 elif self.clientLocalFetcher is not None and idCheckers.IsLocalIdentity(key): 525 localKeysToGet.add(key) 526 527 self.waitingKeys = set() 528 keysToGet -= self.knownLuzers 529 localKeysToGet -= self.knownLuzers 530 keysToGet -= keysIAlreadyHave 531 if len(localKeysToGet): 532 localFetch = getattr(cfg, self.clientLocalFetcher) 533 fk = localFetch(list(keysToGet)) 534 if isinstance(fk, Exception): 535 raise fk 536 if type(fk) == types.TupleType: 537 self.header, data = fk 538 elif hasattr(fk, 'columns'): 539 self.header = fk.columns 540 data = fk 541 else: 542 data = [] 543 cfg.LogWarn('Recordset: ', self.clientLocalFetcher, ' returned an empty record set from local data. Attempting to fetch from server to compensate!') 544 if self.keyidx is None: 545 self.keyidx = self.header.index(self.keycolumn) 546 ix = self.keyidx 547 for i in data: 548 self.data[i[ix]] = i 549 keysToGet.remove(i[ix]) 550 551 if len(keysToGet) == 0: 552 return 553 conf = cfg.GetConfigSvc() 554 fetch = getattr(conf, self.dbMultiFetcher) 555 fk = fetch(list(keysToGet)) 556 if isinstance(fk, Exception): 557 raise fk 558 if type(fk) == types.TupleType: 559 self.header, data = fk 560 elif hasattr(fk, '__columns__'): 561 self.header = fk.__columns__ 562 data = [list(fk)] 563 elif hasattr(fk, 'columns'): 564 self.header = fk.columns 565 data = fk 566 else: 567 raise RuntimeError('_Prime called with unsupported data type', fk.__class__) 568 if self.keyidx is None: 569 self.keyidx = self.header.index(self.keycolumn) 570 ix = self.keyidx 571 for i in data: 572 self.data[i[ix]] = i 573 keysToGet.remove(i[ix]) 574 575 for luzer in keysToGet: 576 self.knownLuzers.add(luzer) 577 log.general.Log('Failed to prime ' + strx(luzer), 1, 1) 578 579 def __PopulateDataset(self, data, key = None, factory = None): 580 if hasattr(data, 'columns'): 581 ix = data.columns.index(self.keycolumn) 582 else: 583 ix = self.header.index(self.keycolumn) 584 gotit = 0 585 for i in data: 586 if factory is not None: 587 i = factory(i) 588 if i[ix] not in self.data: 589 self.data[i[ix]] = i 590 if key and i[ix] == key: 591 gotit = 1 592 blue.pyos.BeNice() 593 594 if key and not gotit: 595 self.knownLuzers.add(key) 596 log.LogTraceback() 597 598 def RemoveFromKnowLuzers(self, key): 599 self.knownLuzers.discard(key) 600 601 def Get(self, key, flush = 0): 602 key = int(key) 603 if flush or key not in self.data: 604 if boot.role != 'server': 605 self.Prime([key]) 606 else: 607 uthread.Lock(self, key) 608 try: 609 if flush or key not in self.data: 610 self.__LoadData(key) 611 finally: 612 uthread.UnLock(self, key) 613 614 if key in self.data: 615 return self.rowclass(self, key) 616 raise KeyError('RecordNotFound', key) 617 618 def GetIfExists(self, key): 619 if key in self.data: 620 return self.rowclass(self, key) 621 else: 622 return None 623 624 def xxx__getitem__(self, index): 625 if index > len(self.data): 626 raise RuntimeError('RecordNotLoaded') 627 return self.rowclass(self, self.data.keys()[index]) 628 629 def __getitem__(self, key): 630 return self.rowclass(self, key) 631 632 def __iter__(self): 633 634 class RecordsetIterator: 635 636 def next(self): 637 return self.rowset.rowclass(self.rowset, self.iter.next()) 638 639 it = RecordsetIterator() 640 it.rowset = self 641 it.iter = self.data.iterkeys() 642 return it 643 644 def __contains__(self, key): 645 return key in self.data 646 647 def GetFetcherString(self): 648 if self.clientLocalFetcher is not None: 649 if self.dbMultiFetcher is not None: 650 fetchers = '(Local: %s - Multi: %s)' % (self.clientLocalFetcher, self.dbMultiFetcher) 651 else: 652 fetchers = '(Local: %s)' % self.clientLocalFetcher 653 else: 654 fetchers = '(Multi: %s)' % self.dbMultiFetcher 655 return fetchers 656 657 658 class Row(utilRow): 659 __guid__ = 'cfg.Row' 660 __persistvars__ = ['header', 'line', 'id'] 661 __slots__ = ('id',) 662 663 def __init__(self, recordset = None, key = None): 664 if recordset == None: 665 self.header = None 666 self.line = None 667 self.id = None 668 else: 669 self.header = recordset.header 670 self.line = recordset.data[key] 671 self.id = key 672 673 def __setattr__(self, name, value): 674 if name in Row.__slots__ or name in utilRow.__slots__: 675 object.__setattr__(self, name, value) 676 else: 677 raise RuntimeError('ReadOnly', name) 678 679 def __setitem__(self, key, value): 680 raise RuntimeError('ReadOnly', key) 681 682 def __str__(self): 683 return self.__repr__() 684 685 def __repr__(self): 686 try: 687 ret = '<Instance of class ' + self.__guid__ + '>\r\n' 688 header = self.header 689 fields = self.line 690 for i in xrange(len(header)): 691 ret = ret + '%s:%s%s\r\n' % (header[i], ' ' * (23 - len(header[i])), fields[i]) 692 693 return ret 694 except: 695 sys.exc_clear() 696 return '<Instance of Row containing crappy data>' 697 698 def __coerce__(self, object): 699 if type(object) == type(0): 700 return (self.id, object) 701 702 def __int__(self): 703 return self.id