Ejemplo n.º 1
0
class RequestQueue(object):
    """
    This class represents abstract request queue. It has two basic operations -
    adding request to the queue and retrieving request to be sent just now.
    Different implementation may perform different algorithms of determining a
    the to be sent first.
    """
    def __init__(self):
        self._wait = Channel()

    def add(self, request):
        "Add request to the queue"
        if self._wait.has_receiver():
            self._wait.send(request)
        else:
            self.queue_add(request)

    def retrieve(self):
        "Retrieve request to be sent. If no requests in the queue sleep until request arrives"
        request = self.queue_retrieve()
        if request is None:
            request = self._wait.receive()
        return request

    def find(self, primary_key):
        "Find request in the internal queue by its primary key"

    def queue_add(self, request):
        "Add request to the internal queue"

    def queue_retrieve(self, request):
        "Retrieve request from the internal queue. If no request in the internal queue return None"
Ejemplo n.º 2
0
class RequestQueue(object):
    """
    This class represents abstract request queue. It has two basic operations -
    adding request to the queue and retrieving request to be sent just now.
    Different implementation may perform different algorithms of determining a
    the to be sent first.
    """
    def __init__(self):
        self._wait = Channel()

    def add(self, request):
        "Add request to the queue"
        if self._wait.has_receiver():
            self._wait.send(request)
        else:
            self.queue_add(request)

    def retrieve(self):
        "Retrieve request to be sent. If no requests in the queue sleep until request arrives"
        request = self.queue_retrieve()
        if request is None:
            request = self._wait.receive()
        return request

    def find(self, primary_key):
        "Find request in the internal queue by its primary key"

    def queue_add(self, request):
        "Add request to the internal queue"

    def queue_retrieve(self, request):
        "Retrieve request from the internal queue. If no request in the internal queue return None"
Ejemplo n.º 3
0
 def testHasReceiver(self):
     
     test_channel = Channel()
     
     def receiver():
         test_channel.receive()
     
     self.assertEquals(False, test_channel.has_receiver())
     
     r = Tasklet.new(receiver)()
     
     Tasklet.sleep(1.0)
     
     self.assertEquals(True, test_channel.has_receiver())
     
     r.kill()
     
     Tasklet.sleep(1.0)
     
     self.assertEquals(False, test_channel.has_receiver())
Ejemplo n.º 4
0
    def testHasReceiver(self):

        test_channel = Channel()

        def receiver():
            test_channel.receive()

        self.assertEquals(False, test_channel.has_receiver())

        r = Tasklet.new(receiver)()

        Tasklet.sleep(1.0)

        self.assertEquals(True, test_channel.has_receiver())

        r.kill()

        Tasklet.sleep(1.0)

        self.assertEquals(False, test_channel.has_receiver())
Ejemplo n.º 5
0
    def testLoop(self):
        recvd = []

        def looper(channel):
            res = channel.receive()
            if res == None:
                raise Exception(
                    "this is an expected exception!! (not a failed test...)")
            else:
                recvd.append(res)

        looper_channel = Channel()
        looper_task = Tasklet.loop(looper)(looper_channel)

        for i in range(10):
            looper_channel.send(i)
        self.assertEqual(range(10), recvd)

        self.assertEqual(-1, looper_channel.balance)

        self.assertTrue(looper_task.alive)

        looper_channel.send(None)  #will trigger exception loop

        #must still be working
        recvd = []
        for i in range(10):
            looper_channel.send(i)
        self.assertEqual(range(10), recvd)

        self.assertEqual(-1, looper_channel.balance)

        looper_task.kill()

        self.assertEqual(0, looper_channel.balance)

        #assert that looper exitted, because it is not receiving anymore
        self.assertFalse(looper_channel.has_receiver())

        self.assertFalse(looper_task.alive)
Ejemplo n.º 6
0
    def testLoop(self):
        recvd = []
        def looper(channel):
            res = channel.receive()
            if res == None:
                raise Exception("some exception")
            else:
                recvd.append(res)
            
        looper_channel = Channel()
        looper_task = Tasklet.loop(looper)(looper_channel)

        for i in range(10):
            looper_channel.send(i)
        self.assertEqual(range(10), recvd)

        self.assertEqual(-1, looper_channel.balance)

        self.assertTrue(looper_task.alive)
        
        looper_channel.send(None) #will trigger exception loop
        
        #must still be working
        recvd = []
        for i in range(10):
            looper_channel.send(i)
        self.assertEqual(range(10), recvd)

        self.assertEqual(-1, looper_channel.balance)
        
        looper_task.kill()

        self.assertEqual(0, looper_channel.balance)
        
        #assert that looper exitted, because it is not receiving anymore
        self.assertFalse(looper_channel.has_receiver())

        self.assertFalse(looper_task.alive)
Ejemplo n.º 7
0
class Combat(mg.constructor.ConstructorModule, CombatParamsContainer):
    "Combat is the combat itself. It is created in the combat daemon process."

    system_params = set(["stage", "title", "time", "timetext"])
    def __init__(self, app, uuid, rules, fqn="mg.mmorpg.combats.core.Combat"):
        mg.constructor.ConstructorModule.__init__(self, app, fqn)
        CombatParamsContainer.__init__(self)
        self.members = []
        self.log = None
        self.member_id = 0
        self.rules = rules
        self.uuid = uuid
        self.controllers = []
        self.rulesinfo = self.conf("combats-%s.rules" % rules, {})
        self.paramsinfo = self.conf("combats-%s.params" % rules, {})
        self.actionsinfo = self.conf("combats-%s.actions" % rules, [])
        self.commands = []
        self.wakeup_channel = Channel()
        self.running_actions = []
        self.ready_actions = []
        self._turn_order_check = False
        self._check_end_condition = True
        self.not_delivered_log = []
        self.start_time = time.time()
        self._flags = set()
        self._textlog_ring = []

    def script_code(self, tag):
        "Get combat script code (syntax tree)"
        return self.conf("combats-%s.script-%s" % (self.rules, tag), [])

    def join(self, member):
        "Join member to the combat"
        self.member_id += 1
        member.id = self.member_id
        self.members.append(member)
        # script event
        globs = self.globs()
        globs["member"] = member
        self.execute_script("joined", globs, lambda: self._("Member joined script"))
        # if combat is started already, notify all other members
        if self.running:
            for controller in self.controllers:
                if controller.connected:
                    controller.deliver_member_joined(member)
        # register member's controllers
        for controller in member.controllers:
            self.add_controller(controller)
        # log join
        self.syslog({
            "type": "join",
            "member": member.id,
            "text": self._("<b>[{time}]</b> Member {id} ({name}) has joined team {team}").format(
                time=self.now(),
                id=member.id,
                name=member.name,
                team=member.team,
            ),
            "cls": "combat-syslog-joined",
        })

    def member(self, memberId):
        for m in self.members:
            if m.id == memberId:
                return m
        return None

    def close(self):
        "Notify combat about it's terminated and about to be destroyed"
        if self.log:
            self.log.close()
        self.flush()

    @property
    def actions(self):
        "Dictionary of available combat actions"
        try:
            return self._actions
        except AttributeError:
            pass
        self._actions = {}
        for act in self.conf("combats-%s.actions" % self.rules, []):
            self._actions[act["code"]] = act
        return self._actions

    @property
    def running(self):
        "True when combat is running"
        return self.stage != "init"

    def run(self, turn_order):
        """
        Run combat (switch to 'combat' stage).
        turn_order - CombatTurnOrder object
        """
        if self.running:
            raise CombatAlreadyRunning(self._("Combat was started twice"))
        self.turn_order = turn_order
        self.set_stage("combat")
        self.log_combat_time()
        # execute start script
        globs = self.globs()
        self.execute_script("start", globs, lambda: self._("Combat start script"))
        # notify all members
        for member in self.members:
            member.started()
        # notify turn order manager
        if self.stage_flag("actions"):
            self.turn_order.start()

    @property
    def stage(self):
        return self._params.get("stage", "init")

    def set_stage(self, stage):
        "Switch combat stage"
        if self.stages.get(stage) is None:
            raise CombatInvalidStage(self._("Combat stage '%s' is not defined") % stage)
        self.set_param("stage", stage)
        self.syslog({
            "type": "stage",
            "stage": stage,
            "text": self._("Combat stage: %s") % stage,
            "cls": "combat-syslog-stage",
        })
        self.wakeup()

    @property
    def flags(self):
        return self._flags

    def set_flags(self, flags):
        self._flags = set(flags)

    @property
    def title(self):
        return self._params.get("title", self._("Combat"))

    def set_title(self, title):
        "Set combat title"
        self.set_param("title", title)
        if self.log:
            self.log.set_title(title)
        self.wakeup()

    @property
    def timetext(self):
        time_format = self.rulesinfo.get("time_format", "mmss")
        time = self.time
        if time_format == "mmss":
            return "%d:%02d" % (time / 60, time % 60)
        elif time_format == "num":
            return self.time
        elif time_format == "realhhmmss":
            return self.now_local().split(" ")[1]

    @property
    def time_mode(self):
        try:
            return self._time_mode
        except AttributeError:
            pass
        self._time_mode = self.rulesinfo.get("time_mode", "begin")
        return self._time_mode

    @property
    def time(self):
        return self._params.get("time", 0)

    def log_combat_time(self):
        self.syslog({
            "text": self._("Combat time: %s") % self.time,
            "time": self.time,
            "cls": "combat-syslog-time",
        })
        if self.time_mode == "change":
            self.textlog({
                "text": self.timetext,
                "cls": "combat-log-time-header",
            })

    def add_time(self, val):
        val = intz(val)
        if val < 1:
            return
        self.set_param("time", self.time + val)
        self.set_param("timetext", self.timetext)
        self.log_combat_time()

    def add_controller(self, controller):
        "Register member controller"
        self.controllers.append(controller)

    @property
    def stages(self):
        "Dictionary of stages and their flags"
        try:
            return self._stages
        except AttributeError:
            pass
        val = self.conf("combats-%s.stages" % self.rules)
        if val is None:
            val = {
                "init": {
                },
                "combat": {
                    "actions": True,
                },
                "finish": {
                },
                "done": {
                   "done": True
                }
            }
        self._stages = val
        return val

    def stage_flag(self, flag):
        "Returns flag value of the current stage. If no flag with such code defined return None"
        return self.stages[self.stage].get(flag)

    def stopped(self):
        "Return True when the combat is stopped"
        return self.stage_flag("done")

    def add_command(self, command):
        "Put command to the combat queue to be executed immediately"
        self.commands.append(command)
        self.wakeup()

    def wakeup(self):
        "Wake up main combat loop if it's busy with processing now"
        if self.wakeup_channel.has_receiver():
            self.wakeup_channel.send(None)

    def process(self, timeout=1):
        "Process combat logic"
        if self._turn_order_check:
            self._turn_order_check = False
            self.turn_order.check()
        if self._check_end_condition:
            self._check_end_condition = False
            self.check_end_condition()
        self.process_commands()
        if self.stage_flag("actions"):
            self.process_actions()
        self.heartbeat()
        self.flush()
        try:
            self.wakeup_channel.receive(timeout)
        except TimeoutError:
            self.idle()

    def globs(self):
        return {
            "local": ScriptMemoryObject()
        }

    def execute_script(self, tag, globs, description=None):
        "Execute combat script with given code"
        self.call("combats.execute-script", self, self.script_code(tag), globs, description=description)

    def execute_member_script(self, member, tag, globs, description=None):
        "Execute combat script for given member"
        globs["member"] = member
        self.execute_script(tag, globs, description)
        self.enqueue_check_end_condition()

    def heartbeat(self):
        "Called on every iteration of the main loop"
        globs = self.globs()
        self.for_each_member(self.execute_member_script, "heartbeat-member", globs, lambda: self._("Member heartbeat script"))
        self.execute_script("heartbeat", globs, lambda: self._("Combat heartbeat script"))

    def process_commands(self):
        "Process enqueued commands"
        while self.commands:
            cmd = self.commands.pop(0)
            cmd.execute()

    def idle(self):
        "Do background processing"
        # execute scripts
        globs = self.globs()
        self.execute_script("idle", globs, lambda: self._("Combat idle script"))
        self.for_each_member(self.execute_member_script, "idle-member", globs, lambda: self._("Member idle script"))
        # call idle for all objects
        self.turn_order.idle()
        for member in self.members:
            member.idle()
        # process general timeouts
        elapsed = time.time() - self.start_time
        timeout = self.rulesinfo.get("timeout", 4 * 3600)
        if elapsed > timeout + 600:
            self.warning(self._("Combat %s terminated due to too long timeout"), self.uuid)
            os._exit(0)
        elif self.stage_flag("actions") and elapsed > timeout:
            self.info(self._("Combat %s timed out"), self.uuid)
            self.draw()

    def flush(self):
        "Flush pending messages"
        # deliver changed parameters
        params = self.changed_params()
        if params:
            for controller in self.controllers:
                controller.combat_params_changed(params)
        for member in self.members:
            params = member.changed_params()
            if params:
                for controller in self.controllers:
                    controller.member_params_changed(member, params)
        # commit logs
        if self.log:
            self.log.flush()
        # deliver new log entries
        if self.not_delivered_log:
            for controller in self.controllers:
                controller.deliver_log(self.not_delivered_log)
            self.not_delivered_log = []
        # flush everything to the clients
        for controller in self.controllers:
            controller.flush()
        self.call("stream.flush")

    def set_log(self, log):
        "Attach logging system to the combat"
        self.log = log

    def textlog(self, entry):
        "Add entry to combat log"
        if self.time_mode == "begin":
            entry["text"] = u'<span class="combat-log-time">%s</span> %s' % (self.timetext, entry.get("text", u""))
        self.not_delivered_log.append(entry)
        if self.log:
            self.log.textlog(entry)
        self._textlog_ring.append(entry)
        l = len(self._textlog_ring)
        if l > textlog_ring_size:
            del self._textlog_ring[0:l - textlog_ring_size]

    def syslog(self, entry):
        "Add entry to combat debug log"
        if self.log:
            self.log.syslog(entry)

    def stop(self):
        "Terminate combat"
        self.set_stage("done")

    # Scripting

    def script_attr(self, attr, handle_exceptions=True):
        if attr == "id":
            return self.uuid
        elif attr == "stage":
            return self.stage
        elif attr == "stage_flags":
            return CombatStageFlags(self)
        elif attr == "time":
            return self.time
        elif attr == "timetext":
            return self.timetext
        elif attr == "now":
            return self.now_local()
        # team list
        m = re_team_list.match(attr)
        if m:
            team = intz(m.group(1))
            return self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in self.members if member.team == team])
        # parameters
        m = re_param_attr.match(attr)
        if m:
            return self.param(attr, handle_exceptions)
        if handle_exceptions:
            return None
        else:
            raise AttributeError(attr)

    def script_set_attr(self, attr, val, env):
        if attr == "stage":
            return self.set_stage(val)
        # parameters
        m = re_param_attr.match(attr)
        if m:
            return self.set_param(attr, val)
        raise ScriptRuntimeError(self._("Invalid attribute '%s'") % attr, env)

    def store(self):
        pass

    # Actions

    def execute_action(self, action):
        "Start executing action"
        self.ready_actions.append(action)
    
    def process_actions(self):
        "Process actions logic"
        self.process_ready_actions()
        self.process_stopped_actions()

    def process_ready_actions(self):
        """
        For every ready action call begin() method and move the action to the list
        of running actions
        """
        if self.ready_actions:
            actions = self.ready_actions
            self.ready_actions = []
            for act in actions:
                if act.source.active:
                    act.begin()
                    self.running_actions.append(act)
            self.enqueue_turn_order_check()
            self.actions_started()
            self.enqueue_check_end_condition()

    def process_stopped_actions(self):
        "For every stopped action call end() method and remove the action from the list"
        if self.running_actions:
            i = 0
            while i < len(self.running_actions):
                act = self.running_actions[i]
                if act.stopped():
                    act.end()
                    del self.running_actions[i]
                else:
                    i += 1
            self.enqueue_turn_order_check()
            self.actions_stopped()
            self.enqueue_check_end_condition()

    def enqueue_check_end_condition(self):
        "Enqueue check_end_condition() to be called on the next iteration of the main loop"
        self._check_end_condition = True
        self.wakeup()

    def check_end_condition(self):
        "Check combat end condition (0 or 1 teams active)"
        if self.stage_flag("actions"):
            teams = set()
            for member in self.members:
                if member.active:
                    teams.add(member.team)
            teams = list(teams)
            if len(teams) == 0:
                self.draw()
            elif len(teams) == 1:
                self.victory(teams[0])

    def draw(self):
        "Combat finished with draw"
        self.syslog({
            "type": "draw",
            "text": self._("Combat was a draw"),
            "cls": "combat-syslog-end",
        })
        for member in self.members:
            member.draw()
        globs = self.globs()
        self.for_each_member(self.execute_member_script, "draw-member", globs, lambda: self._("Combat draw script for a member"))
        self.execute_script("draw", globs, lambda: self._("Combat draw script"))

    def victory(self, team):
        "Combat finished with victory of specified team"
        self.syslog({
            "type": "victory",
            "team": team,
            "text": self._("Victory of team {team}").format(
                team=team,
            ),
            "cls": "combat-syslog-end",
        })
        winners_list = []
        loosers_list = []
        first_winner = None
        first_looser = None
        for member in self.members:
            if member.team != team:
                member.defeat()
                loosers_list.append(member)
                if first_looser is None:
                    first_looser = member
        for member in self.members:
            if member.team == team:
                member.victory()
                winners_list.append(member)
                if first_winner is None:
                    first_winner = member
        globs = self.globs()
        globs["winner_team"] = team
        globs["winners_list"] = self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in winners_list])
        globs["loosers_list"] = self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in loosers_list])
        globs["first_winner"] = first_winner
        globs["first_looser"] = first_looser
        globs["winners_count"] = len(winners_list)
        globs["loosers_count"] = len(loosers_list)
        for member in self.members:
            if member.team != team:
                self.execute_member_script(member, "defeat-member", globs, lambda: self._("Combat defeat script for a member"))
        for member in self.members:
            if member.team == team:
                self.execute_member_script(member, "victory-member", globs, lambda: self._("Combat victory script for a member"))
        self.execute_script("victory", globs, lambda: self._("Combat victory script"))

    def notify_stopped(self):
        "Call this method to signal combat that it's finally stopped"
        for member in self.members:
            if member.may_turn:
                member.turn_take()
        for member in self.members:
            member.stopped()

    def actions_started(self):
        "Called after ready actions started"
        globs = self.globs()
        self.for_each_member(self.execute_member_script, "actions-started-member", globs, lambda: self._("Combat actions started script for a member"))
        self.execute_script("actions-started", globs, lambda: self._("Combat actions started script"))

    def actions_stopped(self):
        "Called after ready actions stopped"
        globs = self.globs()
        self.for_each_member(self.execute_member_script, "actions-stopped-member", globs, lambda: self._("Combat actions stopped script for a member"))
        self.execute_script("actions-stopped", globs, lambda: self._("Combat actions stopped script"))

    def enqueue_turn_order_check(self):
        "Ask combat server to call turn_order check() on the next iteration"
        self._turn_order_check = True
        self.wakeup()

    def for_each_member(self, callback, *args, **kwargs):
        "Call callback for every combat member. Member is passed as a first argument"
        for member in self.members:
            callback(member, *args, **kwargs)

    def __unicode__(self):
        return self._("[Combat %s]") % self.uuid

    def __str__(self):
        return utf2str(unicode(self))