Exemple #1
0
class ControllerProtocol(LineReceiver):
    """
    Protocol for dealing with controller requests.
    """

    def connectionMade(self):
        self.logger = ColouredLogger(debug)
        peer = self.transport.getPeer()
        self.logger.debug("Control connection made from %s:%s" % (peer.host, peer.port))
        self.factory, self.controller_factory = self.factory.main_factory, self.factory

    def connectionLost(self, reason):
        peer = self.transport.getPeer()
        self.logger.debug("Control connection lost from %s:%s" % (peer.host, peer.port))

    def sendJson(self, data):
        self.sendLine(simplejson.dumps(data))

    def lineReceived(self, line):
        data = simplejson.loads(line)
        peer = self.transport.getPeer()
        if data['password'] != self.factory.controller_password:
            self.sendJson({"error": "invalid password"})
            self.logger.info("Control: Invalid password %s (%s:%s)" % (data, peer.host, peer.port))
        else:
            command = data['command'].lower()
            try:
                func = getattr(self, "command%s" % command.title())
            except AttributeError:
                self.sendJson({"error": "unknown command %s" % command})
            else:
                self.logger.debug("Control: %s %s (%s:%s)" % (command.upper(), data, peer.host, peer.port))
                try:
                    func(data)
                except Exception, e:
                    self.sendJson({"error": "%s" % e})
                    traceback.print_exc()
Exemple #2
0
class BlockStore(Thread):
    """
    A class which deals with storing the block worlds, flushing them, etc.
    """

    def __init__(self, blocks_path, sx, sy, sz):
        Thread.__init__(self)
        self.x, self.y, self.z = sx, sy, sz
        self.blocks_path = blocks_path
        self.world_name = os.path.basename(os.path.dirname(blocks_path))
        self.in_queue = Queue()
        self.out_queue = Queue()
        self.logger = ColouredLogger()

    def run(self):
        # Initialise variables
        self.physics = False
        self.physics_engine = Physics(self)
        self.raw_blocks = None # Read this from any thread but please update through TASK_BLOCKSET so queued_blocks is updated
        self.running = True
        self.unflooding = False
        self.finite_water = False
        self.queued_blocks = {} # Blocks which need to be flushed into the file.
        self.create_raw_blocks()
        # Start physics engine
        self.physics_engine.start()
        # Main eval loop
        while self.running:
            try:
                # Pop something off the queue
                task = self.in_queue.get()
                # If we've been asked to flush, do so, and say we did.
                if task[0] is TASK_FLUSH:
                    self.flush()
                    self.out_queue.put([TASK_FLUSH])
                # New block?
                elif task[0] is TASK_BLOCKSET:
                    try:
                        self[task[1]] = task[2]
                        if len(task) == 4 and task[3] == True:
                            # Tells the server to update the given block for clients.
                            self.out_queue.put([TASK_BLOCKSET, (task[1][0], task[1][1], task[1][2], task[2])])
                    except AssertionError:
                        self.logger.warning("Tried to set a block at %s in %s!" % (task[1], self.world_name))
                # Asking for a block?
                elif task[0] is TASK_BLOCKGET:
                    self.out_queue.put([TASK_BLOCKGET, task[1], self[task[1]]])
                # Perhaps physics was enabled?
                elif task[0] is TASK_PHYSICSOFF:
                    self.logger.debug("Disabling physics on '%s'..." % self.world_name)
                    self.disable_physics()
                # Or disabled?
                elif task[0] is TASK_PHYSICSON:
                    self.logger.debug("Enabling physics on '%s'..." % self.world_name)
                    self.enable_physics()
                # I can haz finite water tiem?
                elif task[0] is TASK_FWATERON:
                    self.logger.debug("Enabling finite water on '%s'..." % self.world_name)
                    self.finite_water = True
                # Noes, no more finite water.
                elif task[0] is TASK_FWATEROFF:
                    self.logger.debug("Disabling finite water on '%s'..." % self.world_name)
                    self.finite_water = False
                # Do they need to do a Moses?
                elif task[0] is TASK_UNFLOOD:
                    self.logger.debug("Unflood started on '%s'..." % self.world_name)
                    self.unflooding = True
                # Perhaps that's it, and we need to stop?
                elif task[0] is TASK_STOP:
                    self.logger.debug("Stopping block store '%s'..." % self.world_name)
                    self.physics_engine.stop()
                    self.flush()
                    self.logger.debug("Stopped block store '%s'." % self.world_name)
                    return
                # ???
                else:
                    raise ValueError("Unknown BlockStore task: %s" % task)
            except (KeyboardInterrupt, IOError):
                pass

    def enable_physics(self):
        "Turns on physics"
        self.physics = True

    def disable_physics(self):
        "Disables physics, and clears the in-memory store."
        self.physics = False

    def create_raw_blocks(self):
        "Reads in the gzipped data into a raw array"
        # Open the blocks file
        fh = gzip.GzipFile(self.blocks_path)
        self.raw_blocks = array('c')
        # Read off the size header
        fh.read(4)
        # Copy into the array in chunks
        chunk = fh.read(2048)
        while chunk:
            self.raw_blocks.extend(chunk)
            chunk = fh.read(2048)
        fh.close()

    def get_offset(self, x, y, z):
        "Turns block coordinates into a data offset"
        assert 0 <= x < self.x
        assert 0 <= y < self.y
        assert 0 <= z < self.z
        return y * (self.x * self.z) + z * (self.x) + x

    def get_coords(self, offset):
        "Turns a data offset into coordinates"
        x = offset % self.x
        z = (offset // self.x) % self.z
        y = offset // (self.x * self.z)
        return x, y, z

    def message(self, message):
        "Sends a message out to users about this World."
        self.out_queue.put([TASK_MESSAGE, message])

    def __setitem__(self, (x, y, z), block):
        "Set a block in this level to the given value."
        assert isinstance(block, str) and len(block) == 1
        # Save to queued blocks
        offset = self.get_offset(x, y, z)
        self.queued_blocks[offset] = block
        # And directly to raw blocks, if we must
        if self.raw_blocks:
            self.raw_blocks[offset] = block
            # Ask the physics engine if they'd like a look at that
        self.physics_engine.handle_change(offset, block)
Exemple #3
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])
Exemple #4
0
# Arc is copyright 2009-2011 the Arc team and other contributors.
# Arc is licensed under the BSD 2-Clause modified License.
# To view more details, please see the "LICENSING" file in the "docs" folder of the Arc Package.

import sys
from arc.logger import ColouredLogger

debug = (True if "--debug" in sys.argv else False)
logger = ColouredLogger()
logger.debug("Imported arc/ folder.")
del logger
del ColouredLogger
del sys
Exemple #5
0
makedatfile("config/data/titles.dat")

debug = (True if "--debug" in sys.argv else False)
logger = ColouredLogger(debug)

try:
    from colorama import init
except ImportError:
    logger.warn("Colorama is not installed - console colours DISABLED.")
except Exception as e:
    logger.warn("Unable to import colorama: %s" % e)
    logger.warn("Console colours DISABLED.")
else:
    init()
    logger.stdout("&f")
    logger.debug("&fIf you see this, debug mode is &eon&f!")
    logger.info("&fColorama &ainstalled&f - Console colours &cENABLED&f.")

def doExit():
    if os.name == "nt":
        raw_input("\nYou may now close the server console window.")
    else:
        raw_input("\nPlease press enter to exit.")


def main():
    global logger

    logger.info("Starting up &bArc&f v%s" % VERSION)
    factory = ArcFactory(debug)
    try: