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