def parse_prop(prop): if prop.startswith('*'): key, dummy, val = prop[1:].partition(' ') if not val and key != 'code': error('%s must be followed by a value' % (key,)) return None if key == 'portlist': plistkey, dummy, val = val.partition(' ') order = World.portlist_define_order World.portlist_define_order += 1 res = {'type':'portlist', 'plistkey':plistkey, '_templist':[], '_temporder':order} if 'single' in val.split(): res['focus'] = True return res if key == 'move': val = sluggify(val.strip()) return {'type':'move', 'loc':val} elif key == 'focus': val = sluggify(val.strip()) return {'type':'focus', 'key':val} elif key == 'event': return {'type':'event', 'text':val} elif key == 'panic': return {'type':'panic', 'text':val} # theoretically the text is optional elif key == 'text': return {'type':'text', 'text':val} elif key == 'code': return {'type':'code', 'text':val} elif key == 'selfdesc': return {'type':'selfdesc', 'text':val} elif key == 'editstr': return {'type':'editstr', 'key':val} elif key == 'datetime': val = datetime.datetime.strptime(val, '%Y-%m-%d') return datetime.datetime(year=val.year, month=val.month, day=val.day, tzinfo=datetime.timezone.utc) else: error('Unknown special property type: *%s' % (key,)) return None try: propval = ast.literal_eval(prop) # We test-encode the new value to bson, so that we can be strict # and catch errors. dummy = bson.BSON.encode({'val':propval}, check_keys=True) return propval except: pass return {'type':'text', 'text':prop}
def parse_prop(prop): if prop.startswith('*'): key, dummy, val = prop[1:].partition(' ') if not val and key not in ('code', 'gentext'): error('%s must be followed by a value' % (key,)) return None if key == 'portlist': plistkey, dummy, val = val.partition(' ') res = {'type':'portlist', 'plistkey':plistkey, '_templist':[]} if 'single' in val.split(): res['focus'] = True return res if key == 'move': val = sluggify(val.strip()) return {'type':'move', 'loc':val} elif key == 'focus': val = sluggify(val.strip()) return {'type':'focus', 'key':val} elif key == 'event': return {'type':'event', 'text':val} elif key == 'panic': return {'type':'panic', 'text':val} # theoretically the text is optional elif key == 'text': return {'type':'text', 'text':val} elif key == 'gentext': return {'type':'gentext', 'text':val} elif key == 'code': return {'type':'code', 'text':val} elif key == 'selfdesc': return {'type':'selfdesc', 'text':val} elif key == 'editstr': return {'type':'editstr', 'key':val} elif key == 'datetime': val = datetime.datetime.strptime(val, '%Y-%m-%d') return datetime.datetime(year=val.year, month=val.month, day=val.day, tzinfo=datetime.timezone.utc) else: error('Unknown special property type: *%s' % (key,)) return None try: propval = ast.literal_eval(prop) # We test-encode the new value to bson, so that we can be strict # and catch errors. dummy = bson.BSON.encode({'val':propval}, check_keys=True) return propval except: pass return {'type':'text', 'text':prop}
def __init__(self, name, key=None): self.name = name if key is None: self.key = sluggify(name) else: self.key = key self.locid = None self.props = {} self.proplist = []
def get(self, wid): wid = ObjectId(wid) (world, locations) = yield self.find_build_world(wid) worldname = world.get('name', '???') # This array must be handed to the client to construct the pop-up # location menu. locarray = [ {'id':str(loc['_id']), 'name':loc['name']} for loc in locations ] worldprops = [] cursor = self.application.mongodb.worldprop.find({'wid':wid, 'locid':None}, {'key':1, 'val':1}) while (yield cursor.fetch_next): prop = cursor.next_object() worldprops.append(prop) # cursor autoclose worldprops.sort(key=lambda prop:prop['_id']) ### or other criterion? playerprops = [] cursor = self.application.mongodb.wplayerprop.find({'wid':wid, 'uid':None}, {'key':1, 'val':1}) while (yield cursor.fetch_next): prop = cursor.next_object() playerprops.append(prop) # cursor autoclose playerprops.sort(key=lambda prop:prop['_id']) ### or other criterion? encoder = JSONEncoderExtra() worldproparray = encoder.encode(self.export_prop_array(worldprops)) playerproparray = encoder.encode(self.export_prop_array(playerprops)) self.render('build_world.html', wid=str(wid), worldname=worldname, worldnamejs=json.dumps(worldname), worldnameslug=sluggify(worldname), worldcopyable=json.dumps(world.get('copyable', False)), worldinstancing=json.dumps(world.get('instancing', 'standard')), locarray=json.dumps(locarray), locations=locations, worldproparray=worldproparray, playerproparray=playerproparray)
{'key':'globalscopeid', 'val':globalscopeid}, upsert=True) # The admin player, and associated state. adminplayer = db.players.find_one({'admin':True}) if adminplayer: adminuid = adminplayer['_id'] else: print('No admin player exists; creating.') if not opts.admin_email: raise Exception('You must define admin_email in the config file!') adminplayer = { 'name': 'Admin', 'namekey': sluggify('Admin'), 'admin': True, 'email': opts.admin_email, 'pwsalt': binascii.hexlify(os.urandom(8)), 'password': b'x', # cannot use this password until changed 'createtime': datetime.datetime.now(datetime.timezone.utc), } adminplayer.update(initial_config['playerfields']) adminuid = db.players.insert(adminplayer) playstate = { '_id': adminuid, 'iid': None, 'locid': None,
def create_player(self, handler, email, name, password): """ Create a player entry with the given parameters. Also create a session and sign the player in. The name and email should already have been validated and canonicalized, as much as possible. """ if (not self.app.mongodb): raise MessageException('Database not available.') namekey = sluggify(name) # Check for collisions first. try: resname = yield motor.Op(self.app.mongodb.players.find_one, {'name': name}) resnamekey = yield motor.Op(self.app.mongodb.players.find_one, {'namekey': namekey}) resemail = yield motor.Op(self.app.mongodb.players.find_one, {'email': email}) except Exception as ex: raise MessageException('Database error: %s' % ex) if (resname): raise MessageException('The player name %s is already in use.' % (name, )) if (resnamekey): raise MessageException('The player name %s is already in use.' % (resnamekey['name'], )) if (resemail): raise MessageException('That email address is already registered.') # Both the salt and password strings are stored as bytes, although # they'll really be ascii hex digits. pwsalt = self.random_bytes(8) saltedpw = pwsalt + b':' + password cryptpw = hashlib.sha1(saltedpw).hexdigest().encode() player = { 'name': name, 'namekey': namekey, 'email': email, 'pwsalt': pwsalt, 'password': cryptpw, 'createtime': twcommon.misc.now(), } playerfields = yield motor.Op(self.app.mongodb.config.find_one, {'key': 'playerfields'}) if playerfields: player.update(playerfields['val']) uid = yield motor.Op(self.app.mongodb.players.insert, player) if not uid: raise MessageException('Unable to create player.') # Create the playstate entry. playstate = { '_id': uid, 'iid': None, 'locid': None, 'focus': None, } uid = yield motor.Op(self.app.mongodb.playstate.insert, playstate) if not uid: raise MessageException('Unable to create playstate.') # Create a personal scope for the player. scope = { 'type': 'pers', 'uid': uid, } scid = yield motor.Op(self.app.mongodb.scopes.insert, scope) yield motor.Op(self.app.mongodb.players.update, {'_id': uid}, {'$set': { 'scid': scid }}) # And give the player full access to it yield motor.Op(self.app.mongodb.scopeaccess.insert, { 'uid': uid, 'scid': scid, 'level': twcommon.access.ACC_FOUNDER }) # Create a personal portlist (booklet) for the player. portlist = { 'type': 'pers', 'uid': uid, } plistid = yield motor.Op(self.app.mongodb.portlists.insert, portlist) yield motor.Op(self.app.mongodb.players.update, {'_id': uid}, {'$set': { 'plistid': plistid }}) # Create the first entry for the portlist. try: yield self.create_starting_portal(plistid, scid) except Exception as ex: self.app.twlog.error('Error creating player\'s first portal: %s', ex) # Create a sign-in session too, and we're done. sessionid = yield tornado.gen.Task(self.create_session, handler, uid, email, name) return sessionid
def parse_world(filename): world = World() curloc = None curprop = None fl = open(filename) while True: ln = fl.readline() if not ln: break ln = ln.rstrip() isindent = 0 val = ln.lstrip() if len(val) < len(ln): isindent = len(ln) - len(val) ln = val if not ln or ln.startswith('#'): continue if ln.startswith('***'): break if ln.startswith('*'): # New location. curprop = None lockey, dummy, locname = ln[1:].partition(':') lockey = lockey.strip() locname = locname.strip() if not locname: locname = lockey lockey = sluggify(locname) if lockey in world.locations: error('Location defined twice: %s' % (lockey,)) curloc = Location(locname, lockey) world.locations[lockey] = curloc world.locationlist.append(lockey) curprop = 'desc' continue if isindent and curprop is not None: ### Fails to handle extending player props if not curloc: if curprop not in world.proplist: world.proplist.append(curprop) append_to_prop(world.props, curprop, ln, indent=isindent) else: if curprop not in curloc.proplist: curloc.proplist.append(curprop) append_to_prop(curloc.props, curprop, ln, indent=isindent) continue key, dummy, val = ln.partition(':') if not dummy: error('Line does not define a property: %s' % (ln[:36],)) continue key = key.strip() val = val.strip() if not curloc and key.startswith('$'): curprop = None if key == '$wid': world.wid = val elif key == '$name': world.name = val elif key == '$creator': world.creator = val elif key == '$copyable': world.copyable = not (val.lower()[0] in ['0', 'n', 'f']) elif key == '$instancing': world.instancing = val if val not in ('shared', 'solo', 'standard'): error('$instancing value must be shared, solo, or standard') elif key.startswith('$player.'): key = key[8:].strip() propval = parse_prop(val) if key in world.playerprops: error('Player key defined twice: %s' % (key,)) world.playerprops[key] = propval world.playerproplist.append(key) curprop = key continue else: error('Unknown $key: %s' % (key,)) continue if not key.isidentifier(): error('Property key is not valid: %s' % (key,)) propval = parse_prop(val) if not curloc: if key in world.props: error('World key defined twice: %s' % (key,)) world.props[key] = propval world.proplist.append(key) curprop = key else: if key in curloc.props: error('Location key defined twice in %s: %s' % (curloc.key, key,)) curloc.props[key] = propval curloc.proplist.append(key) curprop = key fl.close() world.check_symbols_used() return world
def create_player(self, handler, email, name, password): """ Create a player entry with the given parameters. Also create a session and sign the player in. The name and email should already have been validated and canonicalized, as much as possible. """ if (not self.app.mongodb): raise MessageException('Database not available.') namekey = sluggify(name) # Check for collisions first. try: resname = yield motor.Op(self.app.mongodb.players.find_one, { 'name': name }) resnamekey = yield motor.Op(self.app.mongodb.players.find_one, { 'namekey': namekey }) resemail = yield motor.Op(self.app.mongodb.players.find_one, { 'email': email }) except Exception as ex: raise MessageException('Database error: %s' % ex) if (resname): raise MessageException('The player name %s is already in use.' % (name,)) if (resnamekey): raise MessageException('The player name %s is already in use.' % (resnamekey['name'],)) if (resemail): raise MessageException('That email address is already registered.') # Both the salt and password strings are stored as bytes, although # they'll really be ascii hex digits. pwsalt = self.random_bytes(8) saltedpw = pwsalt + b':' + password cryptpw = hashlib.sha1(saltedpw).hexdigest().encode() player = { 'name': name, 'namekey': namekey, 'email': email, 'pwsalt': pwsalt, 'password': cryptpw, 'createtime': twcommon.misc.now(), } playerfields = yield motor.Op(self.app.mongodb.config.find_one, {'key':'playerfields'}) if playerfields: player.update(playerfields['val']) uid = yield motor.Op(self.app.mongodb.players.insert, player) if not uid: raise MessageException('Unable to create player.') # Create the playstate entry. playstate = { '_id': uid, 'iid': None, 'locid': None, 'focus': None, } uid = yield motor.Op(self.app.mongodb.playstate.insert, playstate) if not uid: raise MessageException('Unable to create playstate.') # Create a personal scope for the player. scope = { 'type': 'pers', 'uid': uid, } scid = yield motor.Op(self.app.mongodb.scopes.insert, scope) yield motor.Op(self.app.mongodb.players.update, {'_id':uid}, {'$set': {'scid': scid}}) # And give the player full access to it yield motor.Op(self.app.mongodb.scopeaccess.insert, {'uid':uid, 'scid':scid, 'level':twcommon.access.ACC_FOUNDER}) # Create a personal portlist (booklet) for the player. portlist = { 'type': 'pers', 'uid': uid, } plistid = yield motor.Op(self.app.mongodb.portlists.insert, portlist) yield motor.Op(self.app.mongodb.players.update, {'_id':uid}, {'$set': {'plistid': plistid}}) # Create the first entry for the portlist. try: res = yield motor.Op(self.app.mongodb.config.find_one, {'key':'firstportal'}) firstportal = None if res: firstportal = res['val'] if not firstportal: res = yield motor.Op(self.app.mongodb.config.find_one, {'key':'startworldid'}) portwid = res['val'] res = yield motor.Op(self.app.mongodb.config.find_one, {'key':'startworldloc'}) portlockey = res['val'] res = yield motor.Op(self.app.mongodb.locations.find_one, {'wid':portwid, 'key':portlockey}) portlocid = res['_id'] portscid = scid # from above else: portwid = firstportal['wid'] portlocid = firstportal['locid'] portscid = firstportal['scid'] if portscid == 'global': res = yield motor.Op(self.app.mongodb.config.find_one, {'key':'globalscopeid'}) portscid = res['val'] elif portscid == 'personal': portscid = scid # from above if not (portwid and portscid and portlocid): raise Exception('Unable to define portal') portal = { 'plistid':plistid, 'iid':None, 'listpos':1.0, 'wid':portwid, 'scid':portscid, 'locid':portlocid, } yield motor.Op(self.app.mongodb.portals.insert, portal) except Exception as ex: self.app.twlog.error('Error creating player\'s first portal: %s', ex) # Create a sign-in session too, and we're done. sessionid = yield tornado.gen.Task(self.create_session, handler, uid, email, name) return sessionid
def upgrade_to_v2(): print("Upgrading to v2...") cursor = db.players.find({}, {"name": 1}) for player in cursor: namekey = sluggify(player["name"]) db.players.update({"_id": player["_id"]}, {"$set": {"namekey": namekey}})
def get(self, wid): wid = ObjectId(wid) (world, locations) = yield self.find_build_world(wid) # The handling of this export stuff is a nuisance. I don't want # to load the entire world data set into memory. But the json # module isn't set up for yieldy output. # # Therefore, evil hackery! We make assumptions about the formatting # of json.dump output, and stick in stuff iteratively. This requires # care with commas, because the format of JSON is annoying. rootobj = collections.OrderedDict() rootobj['name'] = world.get('name', '???') rootobj['wid'] = str(wid) if 'creator' in world: rootobj['creator_uid'] = str(world['creator']) player = yield motor.Op(self.application.mongodb.players.find_one, { '_id':world['creator'] }, { 'name':1 }) if player: rootobj['creator'] = player['name'] if 'copyable' in world: rootobj['copyable'] = world['copyable'] if 'instancing' in world: rootobj['instancing'] = world['instancing'] rootdump = json.dumps(rootobj, indent=True, ensure_ascii=False) assert rootdump.endswith('\n}') rootdumphead, rootdumptail = rootdump[0:-2], rootdump[-2:] slugname = sluggify(rootobj['name']) self.set_header("Content-Type", "application/json; charset=UTF-8") self.set_header("Content-Disposition", "attachment; filename=%s.json" % (slugname,)) self.write(rootdumphead) encoder = JSONEncoderExtra(indent=True, sort_keys=True, ensure_ascii=False) worldprops = [] cursor = self.application.mongodb.worldprop.find({'wid':wid, 'locid':None}, {'key':1, 'val':1}) while (yield cursor.fetch_next): prop = cursor.next_object() worldprops.append(prop) # cursor autoclose if worldprops: worldprops.sort(key=lambda prop:prop['_id']) ### or other criterion? for prop in worldprops: del prop['_id'] res = encoder.encode(worldprops) self.write(',\n "realmprops": ') self.write(res) playerprops = [] cursor = self.application.mongodb.wplayerprop.find({'wid':wid, 'uid':None}, {'key':1, 'val':1}) while (yield cursor.fetch_next): prop = cursor.next_object() playerprops.append(prop) # cursor autoclose if playerprops: playerprops.sort(key=lambda prop:prop['_id']) ### or other criterion? for prop in playerprops: del prop['_id'] res = encoder.encode(playerprops) self.write(',\n "playerprops": ') self.write(res) self.write(',\n "locations": [\n') for ix, loc in enumerate(locations): locobj = collections.OrderedDict() locobj['key'] = loc['key'] locobj['name'] = loc.get('name', '???') locdump = json.dumps(locobj, indent=True, ensure_ascii=False) assert locdump.endswith('\n}') locdumphead, locdumptail = locdump[0:-2], locdump[-2:] self.write(locdumphead) locprops = [] cursor = self.application.mongodb.worldprop.find({'wid':wid, 'locid':loc['_id']}, {'key':1, 'val':1}) while (yield cursor.fetch_next): prop = cursor.next_object() locprops.append(prop) # cursor autoclose if locprops: locprops.sort(key=lambda prop:prop['_id']) ### or other criterion? for prop in locprops: del prop['_id'] res = encoder.encode(locprops) self.write(',\n "props": ') self.write(res) self.write(locdumptail) if ix < len(locations)-1: self.write(',\n') else: self.write('\n') self.write(' ]') self.write(rootdumptail) self.write('\n')
def post(self): try: name = self.get_argument('name') value = self.get_argument('val') wid = ObjectId(self.get_argument('world')) locid = self.get_argument('loc', None) if locid: locid = ObjectId(locid) (world, loc) = yield self.check_world_arguments(wid, locid) if name == 'lockey': if not locid: raise Exception('No location declared') value = sluggify(value) if not re_valididentifier.match(value): raise Exception('Invalid key name') oloc = yield motor.Op(self.application.mongodb.locations.find_one, { 'wid':wid, 'key':value }) if oloc and oloc['_id'] != locid: raise Exception('A location with this key already exists.') yield motor.Op(self.application.mongodb.locations.update, { '_id':locid }, { '$set':{'key':value} }) self.write( { 'val':value } ) return if name == 'locname': if not locid: raise Exception('No location declared') yield motor.Op(self.application.mongodb.locations.update, { '_id':locid }, { '$set':{'name':value} }) ### dependency change for location name? self.write( { 'val':value } ) return if name == 'worldname': yield motor.Op(self.application.mongodb.worlds.update, { '_id':wid }, { '$set':{'name':value} }) self.write( { 'val':value } ) return if name == 'worldinstancing': value = value.lower() if value not in ("solo", "shared", "standard"): raise Exception('Instancing must be "solo", "shared", or "standard"') yield motor.Op(self.application.mongodb.worlds.update, { '_id':wid }, { '$set':{'instancing':value} }) self.write( { 'val':value } ) return if name == 'worldcopyable': value = value.lower() if value not in ("true", "false"): raise Exception('Copyable must be "true" or "false"') value = (value == "true") yield motor.Op(self.application.mongodb.worlds.update, { '_id':wid }, { '$set':{'copyable':value} }) self.write( { 'val':value } ) return if name == 'copyportal': if not locid: raise Exception('No location declared') uid = self.twsession['uid'] # The server will have to figure out scope. msg = { 'cmd':'buildcopyportal', 'uid':str(uid), 'locid':str(locid), 'wid':str(wid) } self.application.twservermgr.tworld_write(0, msg) # Any failure in this request will not be returned to the # client. Oh well. self.write( { 'ok':True } ) return raise Exception('Data not recognized: %s' % (name,)) except Exception as ex: # Any exception that occurs, return as an error message. self.application.twlog.warning('Caught exception (setting data): %s', ex) self.write( { 'error': str(ex) } )
def post(self): try: key = self.get_argument('key') propid = ObjectId(self.get_argument('id')) wid = ObjectId(self.get_argument('world')) locid = self.get_argument('loc') if locid == '$realm': locid = None elif locid == '$player': pass # special case else: locid = ObjectId(locid) (world, loc) = yield self.check_world_arguments(wid, locid, playerok=True) key = sluggify(key) if not re_valididentifier.match(key): raise Exception('Invalid key name') # Construct the new property, except for the value if loc == '$player': prop = { '_id':propid, 'key':key, 'wid':wid, 'uid':None } else: prop = { '_id':propid, 'key':key, 'wid':wid, 'locid':locid } trashprop = None # Fetch the current version of the property (possibly None). # If that exists, create an entry for the trashprop queue. # Also check for a version with the same key-name (also may be # None). if loc == '$player': # We can only edit all-player wplayerprops here. oprop = yield motor.Op(self.application.mongodb.wplayerprop.find_one, { '_id':propid }) kprop = yield motor.Op(self.application.mongodb.wplayerprop.find_one, { 'wid':wid, 'uid':None, 'key':key }) if oprop: try: trashprop = { 'wid':oprop['wid'], 'uid':oprop['uid'], 'key':oprop['key'], 'val':oprop['val'], 'origtype':'wplayerprop', 'changed':twcommon.misc.now(), } except: pass else: oprop = yield motor.Op(self.application.mongodb.worldprop.find_one, { '_id':propid }) kprop = yield motor.Op(self.application.mongodb.worldprop.find_one, { 'wid':wid, 'locid':locid, 'key':key }) if oprop: try: trashprop = { 'wid':oprop['wid'], 'locid':oprop['locid'], 'key':oprop['key'], 'val':oprop['val'], 'origtype':'worldprop', 'changed':twcommon.misc.now(), } except: pass if self.get_argument('delete', False): if trashprop: try: yield motor.Op(self.application.mongodb.trashprop.insert, trashprop) except Exception as ex: self.application.twlog.warning('Unable to add trashprop: %s', ex) # And now we delete it. if loc == '$player': yield motor.Op(self.application.mongodb.wplayerprop.remove, { '_id':propid }) dependency = ('wplayerprop', wid, None, key) else: yield motor.Op(self.application.mongodb.worldprop.remove, { '_id':propid }) dependency = ('worldprop', wid, locid, key) # Send dependency key to tworld try: encoder = JSONEncoderExtra() depmsg = encoder.encode({ 'cmd':'notifydatachange', 'change':dependency }) self.application.twservermgr.tworld_write(0, depmsg) except Exception as ex: self.application.twlog.warning('Unable to notify tworld of data change: %s', ex) # We have to return all the property information (except for # the value) so the client knows what row to delete. returnprop = {'key':prop['key'], 'id':str(prop['_id'])} self.write( { 'loc':self.get_argument('loc'), 'delete':True, 'prop':returnprop } ) return newval = self.get_argument('val') if len(newval) > 4000: raise Exception('Property value is too long') newval = json.loads(newval) newval = self.import_property(newval) prop['val'] = newval # Make sure this doesn't collide with an existing key (in a # different property). if kprop and kprop['_id'] != propid: raise Exception('A property with that key already exists.') if trashprop: try: yield motor.Op(self.application.mongodb.trashprop.insert, trashprop) except Exception as ex: self.application.twlog.warning('Unable to add trashprop: %s', ex) dependency2 = None # And now we write it. if loc == '$player': yield motor.Op(self.application.mongodb.wplayerprop.update, { '_id':propid }, prop, upsert=True) dependency = ('wplayerprop', wid, None, key) if oprop and key != oprop['key']: dependency2 = ('wplayerprop', wid, None, oprop['key']) else: yield motor.Op(self.application.mongodb.worldprop.update, { '_id':propid }, prop, upsert=True) dependency = ('worldprop', wid, locid, key) if oprop and key != oprop['key']: dependency2 = ('worldprop', wid, locid, oprop['key']) # Send dependency key to tworld try: encoder = JSONEncoderExtra() depmsg = encoder.encode({ 'cmd':'notifydatachange', 'change':dependency }) self.application.twservermgr.tworld_write(0, depmsg) if dependency2: depmsg = encoder.encode({ 'cmd':'notifydatachange', 'change':dependency2 }) self.application.twservermgr.tworld_write(0, depmsg) except Exception as ex: self.application.twlog.warning('Unable to notify tworld of data change: %s', ex) # Converting the value for the javascript client goes through # this array-based call, because I am sloppy like that. returnprop = self.export_prop_array([prop])[0] self.write( { 'loc':self.get_argument('loc'), 'prop':returnprop } ) except Exception as ex: # Any exception that occurs, return as an error message. self.application.twlog.warning('Caught exception (setting property): %s', ex) self.write( { 'error': str(ex) } )
def import_property(self, prop): """Given a type-keyed dict from the client, convert it into database form. Raises an exception if a problem occurs. This is written strictly; it never allows in typed structures that we don't recognize. """ valtype = prop['type'] if valtype == 'value': ### This does not cope with ObjectIds, datetimes, or other ### such items. ### It also allows arbitrary typed dicts, which makes a mockery ### of the strictness I mentioned. val = prop.get('value', None) if not val: raise Exception('Value entry may not be blank') return ast.literal_eval(val) if valtype == 'datetime': val = prop.get('value', None) if not val: return twcommon.misc.now().replace(microsecond=0) val = twcommon.misc.gen_datetime_parse(val) return val if valtype == 'text': res = { 'type':valtype } if 'text' in prop: res['text'] = prop['text'] return res if valtype == 'code': res = { 'type':valtype } if 'text' in prop: res['text'] = prop['text'] return res if valtype == 'event': res = { 'type':valtype } if 'text' in prop: res['text'] = prop['text'] if 'otext' in prop: res['otext'] = prop['otext'] return res if valtype == 'panic': res = { 'type':valtype } if 'text' in prop: res['text'] = prop['text'] if 'otext' in prop: res['otext'] = prop['otext'] return res if valtype == 'move': res = { 'type':valtype } if 'loc' in prop: loc = sluggify(prop['loc']) res['loc'] = loc if 'text' in prop: res['text'] = prop['text'] if 'oleave' in prop: res['oleave'] = prop['oleave'] if 'oarrive' in prop: res['oarrive'] = prop['oarrive'] return res if valtype == 'editstr': res = { 'type':valtype } if 'key' in prop: key = sluggify(prop['key']) res['key'] = key if 'editaccess' in prop: try: editaccess = twcommon.access.level_named(prop['editaccess']) except: namels = twcommon.access.level_name_list() raise Exception('Access level must be in %s' % (namels,)) res['editaccess'] = editaccess if 'label' in prop: res['label'] = prop['label'] if 'text' in prop: res['text'] = prop['text'] if 'otext' in prop: res['otext'] = prop['otext'] return res raise Exception('Unknown property type: %s' % (valtype,))
def upgrade_to_v2(): print('Upgrading to v2...') cursor = db.players.find({}, {'name':1}) for player in cursor: namekey = sluggify(player['name']) db.players.update({'_id':player['_id']}, {'$set':{'namekey':namekey}})
def parse(text): """ Parse a string into a description list -- a list of strings and InterpNodes. This is responsible for finding square brackets and turning them into the correct nodes, according to a somewhat ornate set of rules. (Note unit tests.) """ if type(text) is not str: raise ValueError('interpolated text must be string') res = [] start = 0 curlink = None while (start < len(text)): match = re_bracketgroup.search(text, start) if not match: pos = len(text) else: pos = match.start() append_text_with_paras(res, text, start, pos) if not match: break start = pos numbrackets = match.end() - start if numbrackets == 2: # Read a complete top-level [[...]] interpolation. start = start+2 pos = text.find(']]', start) if (pos < 0): raise ValueError('interpolated text missing ]]') chunk = text[start:pos] res.append(InterpNode.parse(chunk)) start = pos+2 continue start = start+1 if re_initdollar.match(text, start): # Special case: [$foo] is treated the same as [[$foo]]. Not a # link, but an interpolation. pos = text.find(']', start) if (pos < 0): raise ValueError('interpolated $symbol missing ]') chunk = text[start:pos] res.append(InterpNode.parse(chunk)) start = pos+1 continue # Read a [...] or [...|...] link. This (the first part) may # contain a mix of text and interpolations. We may also have # a [...||...] link, in which case the second part is pasted # into the text as well as the target. linkstart = start assert curlink is None curlink = Link() res.append(curlink) while (start < len(text)): match = re_closeorbarorinterp.search(text, start) if not match: raise ValueError('link missing ]') pos = match.start() if text[pos] == ']': append_text_with_paras(res, text, start, pos) chunk = text[linkstart:pos] if Link.looks_url_like(chunk): curlink.target = chunk.strip() curlink.external = True else: curlink.target = sluggify(chunk) res.append(EndLink(curlink.external)) curlink = None start = pos+1 break if text[pos] == '|': append_text_with_paras(res, text, start, pos) start = match.end() doublebar = (start - match.start() > 1) pos = text.find(']', start) if pos < 0: raise ValueError('link | missing ]') chunk = text[start:pos] if doublebar: append_text_with_paras(res, ' '+chunk) curlink.target = sluggify(chunk) else: curlink.target = chunk.strip() curlink.external = Link.looks_url_like(chunk) res.append(EndLink(curlink.external)) curlink = None start = pos+1 break if text[pos] == '[' and pos+1 < len(text) and text[pos+1] != '[': raise ValueError('links cannot be nested') # [[ inside the [ # Read a complete top-level [[...]] interpolation. append_text_with_paras(res, text, start, pos) start = pos+2 pos = text.find(']]', start) if (pos < 0): raise ValueError('interpolated text in link missing ]]') chunk = text[start:pos] res.append(InterpNode.parse(chunk)) start = pos+2 continue return res
db.config.update({"key": "globalscopeid"}, {"key": "globalscopeid", "val": globalscopeid}, upsert=True) # The admin player, and associated state. adminplayer = db.players.find_one({"admin": True}) if adminplayer: adminuid = adminplayer["_id"] else: print("No admin player exists; creating.") if not opts.admin_email: raise Exception("You must define admin_email in the config file!") adminplayer = { "name": "Admin", "namekey": sluggify("Admin"), "admin": True, "email": opts.admin_email, "pwsalt": binascii.hexlify(os.urandom(8)), "password": b"x", # cannot use this password until changed "createtime": datetime.datetime.now(datetime.timezone.utc), } adminplayer.update(initial_config["playerfields"]) adminuid = db.players.insert(adminplayer) playstate = {"_id": adminuid, "iid": None, "locid": None, "focus": None} db.playstate.insert(playstate) scope = {"type": "pers", "uid": adminuid}