Esempio n. 1
0
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}
Esempio n. 2
0
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}
Esempio n. 3
0
 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 = []
Esempio n. 4
0
 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 = []
Esempio n. 5
0
    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)
Esempio n. 6
0
                     {'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,
Esempio n. 7
0
    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
Esempio n. 8
0
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
Esempio n. 9
0
    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
Esempio n. 10
0
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}})
Esempio n. 11
0
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
Esempio n. 12
0
    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')
Esempio n. 13
0
    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) } )
Esempio n. 14
0
    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) } )
Esempio n. 15
0
 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,))
Esempio n. 16
0
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}})
Esempio n. 17
0
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
Esempio n. 18
0
    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}