예제 #1
0
파일: server.py 프로젝트: Jakky89/cuwo
class CubeWorldServer(Factory):
    items_changed = False
    exit_code = 0
    last_secondly_check = None
    updates_since_last_second = 0
    updates_per_second = 0
    current_change_index = 0
    world = None

    def __init__(self, config):
        self.config = config
        base = config.base

        # GAME RELATED
        self.update_packet = ServerUpdate()
        self.update_packet.reset()

        self.connections = set()
        self.players = MultikeyDict()

        self.entities = {}
        self.entity_ids = IDPool(1)

        # DATABASE
        self.db_con = database.get_connection()
        database.create_structure(self.db_con)

        # Initialize default world
        self.world = World(self)

        self.update_loop = LoopingCall(self.update)
        self.update_loop.start(1.0 / constants.UPDATE_FPS, False)

        # SERVER RELATED
        self.git_rev = base.get('git_rev', None)

        self.ranks = {}
        for k, v in base.ranks.iteritems():
            self.ranks[k.lower()] = v

        self.scripts = ScriptManager()
        for script in base.scripts:
            self.load_script(script)

        # INGAME TIME
        self.extra_elapsed_time = 0.0
        self.start_time = reactor.seconds()
        self.set_clock('12:00')

        # START LISTENING
        self.listen_tcp(base.port, self)

    def buildProtocol(self, addr):
        con_remain = self.config.base.max_connections_per_ip
        for connection in self.connections:
            if connection.address.host == addr.host:
                con_remain -= 1
                if con_remain <= 0:
                    if con_remain == 0:
                        print '[WARNING] Too many connections from %s, closing...' % addr.host
                    connection.disconnect()
        if con_remain <= 0:
            return
        self.db_con = database.get_connection()
        if database.is_banned_ip(self.db_con, addr.host):
            print '[INFO] Banned client %s tried to join.' % addr.host
            return 'You are banned from this server.'
        if self.scripts.call('on_connection_attempt', address=addr).result is False:
            print '[WARNING] Connection attempt for %s blocked by script!' % addr.host
            return False
        return CubeWorldConnection(self, addr)

    def remove_item(self, chunk_x, chunk_y, index):
        print '[DEBUG] Removing item #%s from chunk %s,%s' % (index, chunk_x, chunk_y)
        chunk = self.world.get_chunk_unscaled(chunk_x, chunk_y)
        ret = chunk.item_list.pop(index)
        self.items_changed = True
        return ret.item_data

    def drop_item(self, item_data, pos):
        print '[DEBUG] Dropping item at %s,%s' % (pos.x, pos.y)
        chunk = self.world.get_chunk_scaled(pos.x, pos.y)
        if len(chunk.item_list) > constants.MAX_ITEMS_PER_CHUNK:
            print '[WARNING] To many items at Chunk(%s,%s)!' % (math.floor(pos.x / constants.CHUNK_SCALE), math.floor(pos.y / constants.CHUNK_SCALE))
            return False
        item = ChunkItemData()
        item.drop_time = 750
        item.scale = 0.1
        item.rotation = 185.0
        item.something3 = item.something5 = item.something6 = 0
        item.pos = pos
        item.item_data = item_data
        chunk.item_list.append(item)
        self.items_changed = True

    def update(self):
        self.scripts.call('update')

        uxtime = reactor.seconds()
        if self.last_secondly_check:
            update_seconds_delta = uxtime - self.last_secondly_check
        else:
            update_seconds_delta = 0

        if update_seconds_delta < 1:
            self.updates_since_last_second += 1
        else:
            ups = math.floor((self.updates_per_second + (self.updates_since_last_second / update_seconds_delta)) / 2)
            self.updates_since_last_second = 0
            if ups != self.updates_per_second:
                dus = ups - self.updates_per_second
                self.updates_per_second = ups
                if dus > 0:
                    print "\rUpdates/s: %s (+%s)" % (ups, dus)
                elif dus < 0:
                    print "\rUpdates/s: %s (-%s)" % (ups, dus)
            for player in self.players.values():
                if player.packet_count > 0:
                    ppr = math.ceil( ( player.packet_rate + ( player.packet_count / update_seconds_delta ) ) / 2 )
                    player.packet_count = 0
                    if ppr != player.packet_rate:
                        dpr = ppr - player.packet_rate
                        player.packet_rate = ppr
                        if dpr > 0:
                            print "\rPackets/s for %s: %s (+%s)" % (player.name, player.packet_rate, dpr)
                        elif dpr < 0:
                            print "\rPackets/s for %s: %s (-%s)" % (player.name, player.packet_rate, dpr)

        # entity updates
        for entity_id, entity in self.entities.iteritems():
            entity_packet.set_entity(entity, entity_id, entity.mask)
            entity.mask = 0
            self.broadcast_packet(entity_packet)
        self.broadcast_packet(update_finished_packet)

        # other updates
        update_packet = self.update_packet
        if self.items_changed:
            for chunk in self.world.chunks.values():
                item_list = ChunkItems()
                item_list.chunk_x = chunk.chunk_x
                item_list.chunk_y = chunk.chunk_y
                item_list.items = chunk.item_list
                update_packet.chunk_items.append(item_list)
                for item in chunk.item_list:
                    item.drop_time = 0
            self.items_changed = False
        self.broadcast_packet(update_packet)
        update_packet.reset()

        if update_seconds_delta != 0:
            for player in self.players.values():
                if player.time_last_packet >= (uxtime - constants.CLIENT_RECV_TIMEOUT):
                    if player.entity_data.changed:
                        player.entity_data.changed = False
                        ret = player.do_anticheat_actions()
                        if (not ret) and player.login_id:
                            database.update_player(self.db_con, player.login_id, player.name)
                else:
                    print '[WARNING] Connection timed out for Player %s #%s' % (player.entity_data.name, player.entity_id)
                    player.kick('Connection timed out')
        self.broadcast_time()

    def hit_entity_id(self, id):
        return True

    def send_chat(self, value):
        packet = ServerChatMessage()
        packet.entity_id = 0
        packet.value = value
        self.broadcast_packet(packet)

    def broadcast_packet(self, packet):
        data = write_packet(packet)
        for player in self.players.values():
            player.transport.write(data)

    def broadcast_time(self):
        time_packet.time = self.get_time()
        time_packet.day = self.get_day()
        self.broadcast_packet(time_packet)

    # line/string formatting options based on config

    def format(self, value):
        format_dict = {'server_name': self.config.base.server_name}
        return value % format_dict

    def format_lines(self, value):
        lines = []
        for line in value:
            lines.append(self.format(line))
        return lines

    # script methods

    def load_script(self, name):
        try:
            mod = __import__('scripts.%s' % name, globals(), locals(), [name])
        except ImportError, e:
            traceback.print_exc(e)
            return None
        script = mod.get_class()(self)
        print '[INFO] Loaded script %r' % name
        return script