Exemple #1
0
 def run(self):
     while self.running:
         if self.blockstore.physics:
             if self.blockstore.unflooding:
                 # Do n fluid removals
                 updates = 0
                 for offset, block in enumerate(self.blockstore.raw_blocks):
                     if block == CHR_LAVA or block == CHR_WATER or block == CHR_SPOUT or block == CHR_LAVA_SPOUT:
                         x, y, z = self.blockstore.get_coords(offset)
                         self.set_block((x, y, z), BLOCK_AIR)
                         updates += 1
                         if updates >= LIMIT_UNFLOOD:
                             break
                 else:
                     # Unflooding complete.
                     self.blockstore.unflooding = False
                     self.blockstore.message(COLOUR_YELLOW + "Unflooding complete.")
                     self.changed.clear()
                     self.working = set()
             else:
                 # If this is the first of a physics run, redo the queues from scratch
                 if not self.was_physics or self.was_unflooding:
                     self.logger.debug("Queue everything for '%s'." % self.blockstore.world_name)
                     self.changed.clear()
                     self.working = Popxrange(0, (self.blockstore.x * self.blockstore.y * self.blockstore.z))
                 # if working list is empty then copy changed set to working set
                 # otherwise keep using the working set till empty
                 elif len(self.working) == 0:
                     self.logger.debug("Performing expand checks for '%s' with %d changes." % (
                     self.blockstore.world_name, len(self.changed)))
                     changedfixed = self.changed # 'changedfixed' is 'changed' so gets all updates
                     self.changed = set()        # until 'changed' is a new set. This and the above statment are ATOMIC
                     self.working = set()         # changes from a Popxrange to a set
                     while len(changedfixed) > 0:
                         self.expand_checks(changedfixed.pop())
                 self.logger.debug("Starting physics run for '%s' with %d checks." % (
                 self.blockstore.world_name, len(self.working)))
                 updates = 0
                 try:
                     for x in xrange(LIMIT_CHECKS):
                         offset = self.working.pop()
                         updates += self.handle(offset)
                 except KeyError:
                     pass
                     #if overflow and (time.time() - self.last_lag > self.LAG_INTERVAL):
                     #self.blockstore.message("Physics is currently lagging in %(id)s.")
                     #self.last_lag = time.time()
                 self.logger.debug("Ended physics run for '%s' with %d updates and %d checks remaining." % (
                 self.blockstore.world_name, updates, len(self.working)))
         else:
             if self.was_physics:
                 self.blockstore.unflooding = False
                 self.changed.clear()
                 self.working = set()
         self.was_physics = self.blockstore.physics
         self.was_unflooding = self.blockstore.unflooding
         # Wait till next iter
         time.sleep(0.7) # TODO change this so takes into account run time
Exemple #2
0
class Physics(Thread):
    """
    Given a BlockStore, works out what needs doing (water, grass etc.)
    and send the changes back to the BlockStore.
    """
    # This thread:
    # * Receives changes by ATOMIC updates to 'changed' eg. self.changed.add(offset)
    # * Sends changes by queueing updates in the blockstore
    # * Reads but doesn't modify self.blockstore.raw_blocks[]
    # Optimised by only checking blocks near changes, advantages are a small work set and very up-to-date

    def __init__(self, blockstore):
        Thread.__init__(self)
        self.blockstore = blockstore
        #self.setDaemon(True) # means that python can terminate even if still active
        #self.work = Event()    # TODO use event to start and stop
        self.last_lag = 0
        self.running = True
        self.was_physics = False
        self.was_unflooding = False
        self.changed = set()
        self.working = set() # could be a list or a sorted list but why bother (world updates may appear in random order but most of the time so many get updated it should be unnoticable)
        self.sponge_locations = set()
        self.logger = ColouredLogger(debug)

    def stop(self):
        self.running = False
        self.join()

    def run(self):
        while self.running:
            if self.blockstore.physics:
                if self.blockstore.unflooding:
                    # Do n fluid removals
                    updates = 0
                    for offset, block in enumerate(self.blockstore.raw_blocks):
                        if block == CHR_LAVA or block == CHR_WATER or block == CHR_SPOUT or block == CHR_LAVA_SPOUT:
                            x, y, z = self.blockstore.get_coords(offset)
                            self.set_block((x, y, z), BLOCK_AIR)
                            updates += 1
                            if updates >= LIMIT_UNFLOOD:
                                break
                    else:
                        # Unflooding complete.
                        self.blockstore.unflooding = False
                        self.blockstore.message(COLOUR_YELLOW + "Unflooding complete.")
                        self.changed.clear()
                        self.working = set()
                else:
                    # If this is the first of a physics run, redo the queues from scratch
                    if not self.was_physics or self.was_unflooding:
                        self.logger.debug("Queue everything for '%s'." % self.blockstore.world_name)
                        self.changed.clear()
                        self.working = Popxrange(0, (self.blockstore.x * self.blockstore.y * self.blockstore.z))
                    # if working list is empty then copy changed set to working set
                    # otherwise keep using the working set till empty
                    elif len(self.working) == 0:
                        self.logger.debug("Performing expand checks for '%s' with %d changes." % (
                        self.blockstore.world_name, len(self.changed)))
                        changedfixed = self.changed # 'changedfixed' is 'changed' so gets all updates
                        self.changed = set()        # until 'changed' is a new set. This and the above statment are ATOMIC
                        self.working = set()         # changes from a Popxrange to a set
                        while len(changedfixed) > 0:
                            self.expand_checks(changedfixed.pop())
                    self.logger.debug("Starting physics run for '%s' with %d checks." % (
                    self.blockstore.world_name, len(self.working)))
                    updates = 0
                    try:
                        for x in xrange(LIMIT_CHECKS):
                            offset = self.working.pop()
                            updates += self.handle(offset)
                    except KeyError:
                        pass
                        #if overflow and (time.time() - self.last_lag > self.LAG_INTERVAL):
                        #self.blockstore.message("Physics is currently lagging in %(id)s.")
                        #self.last_lag = time.time()
                    self.logger.debug("Ended physics run for '%s' with %d updates and %d checks remaining." % (
                    self.blockstore.world_name, updates, len(self.working)))
            else:
                if self.was_physics:
                    self.blockstore.unflooding = False
                    self.changed.clear()
                    self.working = set()
            self.was_physics = self.blockstore.physics
            self.was_unflooding = self.blockstore.unflooding
            # Wait till next iter
            time.sleep(0.7) # TODO change this so takes into account run time

    def handle_change(self, offset, block): # must be ATOMIC
        "Gets called when a block is changed, with its offset and type."
        self.changed.add(offset)

    def set_block(self, (x, y, z), block): # only place blockstore is updated
        "Call to queue a block change, with its position and type."
        self.blockstore.in_queue.put([TASK_BLOCKSET, (x, y, z), chr(block), True])