Esempio n. 1
0
    def sendUpdate(self, do, name, args):
        if not do:
            return
        if not do.dclass:
            return
        field = do.dclass.getFieldByName(name)
        if not field:
            self.notify.warning(
                "Tried to send update for non-existent field %s" % name)
            return
        if field.asParameter():
            self.notify.warning("Tried to send parameter field as a message")
            return

        packer = DCPacker()
        packer.rawPackUint16(NetMessages.B_ObjectMessage)
        packer.rawPackUint32(do.doId)
        packer.rawPackUint16(field.getNumber())

        packer.beginPack(field)
        field.packArgs(packer, args)
        if not packer.endPack():
            self.notify.warning("Failed to pack object message")
            return

        dg = PyDatagram(packer.getBytes())
        self.sendDatagram(dg)
Esempio n. 2
0
    def sendActivate(self, doId, parentId, zoneId, dclass=None, fields=None):
        """
        Activate a DBSS object, given its doId, into the specified parentId/zoneId.

        If both dclass and fields are specified, an ACTIVATE_WITH_DEFAULTS_OTHER
        will be sent instead. In other words, the specified fields will be
        auto-applied during the activation.
        """

        fieldPacker = DCPacker()
        fieldCount = 0
        if dclass and fields:
            for k, v in fields.items():
                field = dclass.getFieldByName(k)
                if not field:
                    self.notify.error(
                        'Activation request for %s object contains '
                        'invalid field named %s' % (dclass.getName(), k))

                fieldPacker.rawPackUint16(field.getNumber())
                fieldPacker.beginPack(field)
                field.packArgs(fieldPacker, v)
                fieldPacker.endPack()
                fieldCount += 1

            dg = PyDatagram()
            dg.addServerHeader(doId, self.ourChannel,
                               DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS)
            dg.addUint32(doId)
            dg.addUint32(0)
            dg.addUint32(0)
            self.send(dg)
            # DEFAULTS_OTHER isn't implemented yet, so we chase it with a SET_FIELDS
            dg = PyDatagram()
            dg.addServerHeader(doId, self.ourChannel,
                               STATESERVER_OBJECT_SET_FIELDS)
            dg.addUint32(doId)
            dg.addUint16(fieldCount)
            dg.appendData(fieldPacker.getString())
            self.send(dg)
            # Now slide it into the zone we expect to see it in (so it
            # generates onto us with all of the fields in place)
            dg = PyDatagram()
            dg.addServerHeader(doId, self.ourChannel,
                               STATESERVER_OBJECT_SET_LOCATION)
            dg.addUint32(parentId)
            dg.addUint32(zoneId)
            self.send(dg)
        else:
            dg = PyDatagram()
            dg.addServerHeader(doId, self.ourChannel,
                               DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS)
            dg.addUint32(doId)
            dg.addUint32(parentId)
            dg.addUint32(zoneId)
            self.send(dg)
    def createObject(self, databaseId, dclass, fields={}, callback=None):
        """
        Create an object in the specified database.

        databaseId specifies the control channel of the target database.
        dclass specifies the class of the object to be created.
        fields is a dict with any fields that should be stored in the object on creation.
        callback will be called with callback(doId) if specified. On failure, doId is 0.
        """

        # Save the callback:
        ctx = self.air.getContext()
        self._callbacks[ctx] = callback

        # Pack up/count valid fields.
        fieldPacker = DCPacker()
        fieldCount = 0
        for k, v in fields.items():
            field = dclass.getFieldByName(k)
            if not field:
                self.notify.error('Creation request for %s object contains '
                                  'invalid field named %s' %
                                  (dclass.getName(), k))

            fieldPacker.rawPackUint16(field.getNumber())
            fieldPacker.beginPack(field)
            field.packArgs(fieldPacker, v)
            fieldPacker.endPack()
            fieldCount += 1

        # Now generate and send the datagram:
        dg = PyDatagram()
        dg.addServerHeader(databaseId, self.air.ourChannel,
                           DBSERVER_CREATE_OBJECT)
        dg.addUint32(ctx)
        dg.addUint16(dclass.getNumber())
        dg.addUint16(fieldCount)
        dg.appendData(fieldPacker.getBytes())
        self.air.send(dg)
Esempio n. 4
0
    def sendUpdate(self, do, name, args, client=None):
        if not do:
            return
        if not do.dclass:
            return

        field = do.dclass.getFieldByName(name)
        if not field:
            self.notify.warning("Tried to send unknown field %s" % name)
            return
        if field.asParameter():
            self.notify.warning("Can't sent parameter field as a message")
            return

        packer = DCPacker()
        packer.rawPackUint16(NetMessages.B_ObjectMessage)
        packer.rawPackUint32(do.doId)
        packer.rawPackUint16(field.getNumber())

        packer.beginPack(field)
        field.packArgs(packer, args)
        if not packer.endPack():
            self.notify.warning("Failed to pack message")
            return

        dg = PyDatagram(packer.getBytes())
        if not client:
            if field.isBroadcast():
                # Send to all interested clients
                for cl in self.zonesToClients.get(do.zoneId, set()):
                    self.sendDatagram(dg, cl.connection)
            else:
                self.notify.warning(
                    "Can't send non-broadcast object message without a target client"
                )
                return
        else:
            self.sendDatagram(dg, client.connection)
    def updateObject(self,
                     databaseId,
                     doId,
                     dclass,
                     newFields,
                     oldFields=None,
                     callback=None):
        """
        Update field(s) on an object, optionally with the requirement that the
        fields must match some old value.

        databaseId and doId represent the database control channel and object ID
        for the update request.
        newFields is to be a dict of fieldname->value, representing the fields
        to add/change on the database object.
        oldFields, if specified, is a similarly-formatted dict that contains the
        expected older values. If the values do not match, the database will
        refuse to process the update. This is useful for guarding against race
        conditions.

        On success, the callback is called as callback(None).
        On failure, the callback is called as callback(dict), where dict contains
        the current object values. This is so that the updater can try again,
        basing its updates off of the new values.
        """

        # Ensure that the keys in newFields and oldFields are the same if
        # oldFields is given...
        if oldFields is not None:
            if set(newFields.keys()) != set(oldFields.keys()):
                self.notify.error(
                    'newFields and oldFields must contain the same keys!')
                return

        fieldPacker = DCPacker()
        fieldCount = 0
        for k, v in newFields.items():
            field = dclass.getFieldByName(k)
            if not field:
                self.notify.error('Update for %s(%d) object contains invalid'
                                  ' field named %s' %
                                  (dclass.getName(), doId, k))

            fieldPacker.rawPackUint16(field.getNumber())

            if oldFields is not None:
                # Pack the old values:
                fieldPacker.beginPack(field)
                field.packArgs(fieldPacker, oldFields[k])
                fieldPacker.endPack()

            fieldPacker.beginPack(field)
            field.packArgs(fieldPacker, v)
            fieldPacker.endPack()

            fieldCount += 1

        # Generate and send the datagram:
        dg = PyDatagram()
        if oldFields is not None:
            ctx = self.air.getContext()
            self._callbacks[ctx] = callback
            if fieldCount == 1:
                dg.addServerHeader(databaseId, self.air.ourChannel,
                                   DBSERVER_OBJECT_SET_FIELD_IF_EQUALS)
            else:
                dg.addServerHeader(databaseId, self.air.ourChannel,
                                   DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS)
            dg.addUint32(ctx)
        else:
            if fieldCount == 1:
                dg.addServerHeader(databaseId, self.air.ourChannel,
                                   DBSERVER_OBJECT_SET_FIELD)
            else:
                dg.addServerHeader(databaseId, self.air.ourChannel,
                                   DBSERVER_OBJECT_SET_FIELDS)
        dg.addUint32(doId)
        if fieldCount != 1:
            dg.addUint16(fieldCount)
        dg.appendData(fieldPacker.getBytes())
        self.air.send(dg)

        if oldFields is None and callback is not None:
            # Why oh why did they ask for a callback if there's no oldFields?
            # Oh well, better honor their request:
            callback(None)