2019-03-01 19:41:40 +00:00
|
|
|
import asyncio
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import pkgutil
|
|
|
|
import importlib
|
|
|
|
|
|
|
|
from Houdini.Spheniscidae import Spheniscidae
|
|
|
|
from Houdini.Penguin import Penguin
|
|
|
|
from Houdini import PenguinStringCompiler
|
|
|
|
import config
|
|
|
|
|
|
|
|
from aiologger import Logger
|
|
|
|
from aiologger.handlers.files import AsyncTimedRotatingFileHandler, RolloverInterval, AsyncFileHandler
|
|
|
|
from aiologger.handlers.streams import AsyncStreamHandler
|
|
|
|
|
|
|
|
import logging
|
|
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
|
|
|
|
import aioredis
|
|
|
|
from aiocache import SimpleMemoryCache
|
|
|
|
from watchdog.observers import Observer
|
|
|
|
|
2019-03-18 15:59:31 +00:00
|
|
|
from Houdini.Data import db
|
2019-04-25 16:13:18 +00:00
|
|
|
from Houdini.Data.Item import ItemCrumbsCollection
|
|
|
|
from Houdini.Data.Igloo import IglooCrumbsCollection, FurnitureCrumbsCollection, \
|
|
|
|
LocationCrumbsCollection, FlooringCrumbsCollection
|
|
|
|
from Houdini.Data.Room import RoomCrumbsCollection
|
|
|
|
from Houdini.Data.Stamp import StampCrumbsCollection
|
|
|
|
from Houdini.Data.Ninja import CardCrumbsCollection
|
|
|
|
from Houdini.Data.Mail import PostcardCrumbsCollection
|
|
|
|
from Houdini.Data.Pet import PuffleCrumbsCollection, PuffleItemCrumbsCollection
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
import uvloop
|
2019-03-19 02:43:25 +00:00
|
|
|
uvloop.install()
|
2019-03-01 19:41:40 +00:00
|
|
|
except ImportError:
|
|
|
|
uvloop = None
|
|
|
|
|
|
|
|
import Houdini.Handlers
|
2019-04-11 00:04:46 +00:00
|
|
|
from Houdini.Handlers import listeners_from_module, remove_handlers_by_module
|
2019-03-01 19:41:40 +00:00
|
|
|
from Houdini.Events.HandlerFileEvent import HandlerFileEventHandler
|
2019-04-11 00:04:46 +00:00
|
|
|
from Houdini.Events.PluginFileEvent import PluginFileEventHandler
|
2019-04-22 00:16:41 +00:00
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
from Houdini.Commands import commands_from_plugin
|
2019-04-22 00:16:41 +00:00
|
|
|
|
2019-04-11 00:04:46 +00:00
|
|
|
import Houdini.Plugins as Plugins
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HoudiniFactory:
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
self.server = None
|
|
|
|
self.redis = None
|
|
|
|
self.config = None
|
|
|
|
self.cache = None
|
2019-03-18 15:59:31 +00:00
|
|
|
self.db = db
|
2019-03-01 19:41:40 +00:00
|
|
|
self.peers_by_ip = {}
|
|
|
|
|
|
|
|
self.server_name = kwargs['server']
|
|
|
|
self.server_config = None
|
|
|
|
|
|
|
|
self.logger = None
|
|
|
|
|
|
|
|
self.client_class = Spheniscidae
|
|
|
|
self.penguin_string_compiler = None
|
2019-04-22 00:17:56 +00:00
|
|
|
self.anonymous_penguin_string_compiler = None
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
self.penguins_by_id = {}
|
|
|
|
self.penguins_by_username = {}
|
|
|
|
|
|
|
|
self.xt_listeners, self.xml_listeners = {}, {}
|
2019-04-22 00:16:41 +00:00
|
|
|
self.commands = {}
|
2019-04-11 00:03:08 +00:00
|
|
|
self.plugins = {}
|
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
self.items = None
|
|
|
|
self.igloos = None
|
|
|
|
self.furniture = None
|
|
|
|
self.locations = None
|
|
|
|
self.flooring = None
|
|
|
|
self.rooms = None
|
|
|
|
self.stamps = None
|
|
|
|
self.cards = None
|
|
|
|
self.postcards = None
|
|
|
|
self.puffles = None
|
|
|
|
self.puffle_items = None
|
|
|
|
|
|
|
|
self.spawn_rooms = None
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
async def start(self):
|
|
|
|
self.config = config
|
|
|
|
|
|
|
|
self.server_config = self.config.servers[self.server_name]
|
|
|
|
|
|
|
|
general_log_directory = os.path.dirname(self.server_config["Logging"]["General"])
|
|
|
|
errors_log_directory = os.path.dirname(self.server_config["Logging"]["Errors"])
|
|
|
|
|
|
|
|
if not os.path.exists(general_log_directory):
|
|
|
|
os.mkdir(general_log_directory)
|
|
|
|
|
|
|
|
if not os.path.exists(errors_log_directory):
|
|
|
|
os.mkdir(errors_log_directory)
|
|
|
|
|
|
|
|
if sys.platform != 'win32':
|
2019-03-19 02:43:00 +00:00
|
|
|
self.logger = Logger(name='Houdini')
|
2019-03-01 19:41:40 +00:00
|
|
|
universal_handler = AsyncTimedRotatingFileHandler(
|
|
|
|
filename=self.server_config['Logging']['General'],
|
|
|
|
backup_count=3,
|
|
|
|
when=RolloverInterval.HOURS
|
|
|
|
)
|
2019-04-25 16:13:18 +00:00
|
|
|
error_handler = AsyncFileHandler(filename=self.server_config['Logging']['Errors'])
|
2019-03-01 19:41:40 +00:00
|
|
|
console_handler = AsyncStreamHandler(stream=sys.stdout)
|
|
|
|
else:
|
|
|
|
self.logger = logging.getLogger('Houdini')
|
|
|
|
universal_handler = RotatingFileHandler(self.server_config['Logging']['General'],
|
|
|
|
maxBytes=2097152, backupCount=3, encoding='utf-8')
|
|
|
|
|
|
|
|
error_handler = logging.FileHandler(self.server_config['Logging']['Errors'])
|
|
|
|
console_handler = logging.StreamHandler(stream=sys.stdout)
|
|
|
|
|
|
|
|
log_formatter = logging.Formatter('%(asctime)s [%(levelname)-5.5s] %(message)s')
|
|
|
|
error_handler.setLevel(logging.ERROR)
|
|
|
|
|
|
|
|
universal_handler.setFormatter(log_formatter)
|
|
|
|
console_handler.setFormatter(log_formatter)
|
|
|
|
|
|
|
|
self.logger.addHandler(universal_handler)
|
|
|
|
self.logger.addHandler(console_handler)
|
2019-04-25 16:13:18 +00:00
|
|
|
self.logger.addHandler(error_handler)
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
level = logging.getLevelName(self.server_config['Logging']['Level'])
|
|
|
|
self.logger.setLevel(level)
|
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
self.server = await asyncio.start_server(
|
|
|
|
self.client_connected, self.server_config['Address'],
|
|
|
|
self.server_config['Port']
|
|
|
|
)
|
|
|
|
|
|
|
|
await self.db.set_bind('postgresql://{}:{}@{}/{}'.format(
|
|
|
|
self.config.database['Username'], self.config.database['Password'],
|
|
|
|
self.config.database['Address'],
|
|
|
|
self.config.database['Name']))
|
|
|
|
|
2019-03-01 19:41:40 +00:00
|
|
|
self.logger.info('Houdini module instantiated')
|
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
|
|
|
self.config.redis['Address'], self.config.redis['Port']),
|
|
|
|
minsize=5, maxsize=10)
|
2019-03-01 19:41:40 +00:00
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
if self.server_config['World']:
|
|
|
|
await self.redis.delete('{}.players'.format(self.server_name))
|
|
|
|
await self.redis.delete('{}.population'.format(self.server_name))
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
self.cache = SimpleMemoryCache(namespace='houdini', ttl=self.server_config['CacheExpiry'])
|
|
|
|
|
|
|
|
self.client_class = Penguin
|
|
|
|
self.penguin_string_compiler = PenguinStringCompiler()
|
2019-04-22 00:17:56 +00:00
|
|
|
self.anonymous_penguin_string_compiler = PenguinStringCompiler()
|
|
|
|
|
|
|
|
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
|
|
|
|
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_penguin_string_compiler)
|
2019-03-01 19:41:40 +00:00
|
|
|
|
2019-04-26 23:12:05 +00:00
|
|
|
self.load_handler_modules(exclude_load='Houdini.Handlers.Login.Login')
|
2019-03-01 19:41:40 +00:00
|
|
|
self.logger.info('World server started')
|
|
|
|
else:
|
2019-04-26 23:12:05 +00:00
|
|
|
self.load_handler_modules('Houdini.Handlers.Login.Login')
|
2019-03-01 19:41:40 +00:00
|
|
|
self.logger.info('Login server started')
|
|
|
|
|
2019-04-25 16:13:18 +00:00
|
|
|
self.items = await ItemCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} clothing items'.format(len(self.items)))
|
|
|
|
|
|
|
|
self.igloos = await IglooCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} igloos'.format(len(self.igloos)))
|
|
|
|
|
|
|
|
self.furniture = await FurnitureCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} furniture items'.format(len(self.furniture)))
|
|
|
|
|
|
|
|
self.locations = await LocationCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} igloo locations'.format(len(self.locations)))
|
|
|
|
|
|
|
|
self.flooring = await FlooringCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} igloo flooring'.format(len(self.flooring)))
|
|
|
|
|
|
|
|
self.rooms = await RoomCrumbsCollection.get_collection()
|
|
|
|
self.spawn_rooms = self.rooms.get_spawn_rooms()
|
|
|
|
await self.rooms.setup_tables()
|
|
|
|
await self.rooms.setup_waddles()
|
|
|
|
self.logger.info('Loaded {} rooms ({} spawn)'.format(len(self.rooms), len(self.spawn_rooms)))
|
|
|
|
|
|
|
|
self.postcards = await PostcardCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} postcards'.format(len(self.postcards)))
|
|
|
|
|
|
|
|
self.stamps = await StampCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} stamps'.format(len(self.stamps)))
|
|
|
|
|
|
|
|
self.cards = await CardCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} ninja cards'.format(len(self.cards)))
|
|
|
|
|
|
|
|
self.puffles = await PuffleCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} puffles'.format(len(self.puffles)))
|
|
|
|
|
|
|
|
self.puffle_items = await PuffleItemCrumbsCollection.get_collection()
|
|
|
|
self.logger.info('Loaded {} puffle care items'.format(len(self.puffle_items)))
|
2019-04-11 00:03:08 +00:00
|
|
|
|
2019-03-01 19:41:40 +00:00
|
|
|
handlers_path = './Houdini{}Handlers'.format(os.path.sep)
|
|
|
|
plugins_path = './Houdini{}Plugins'.format(os.path.sep)
|
2019-04-22 00:16:41 +00:00
|
|
|
self.configure_observers([handlers_path, HandlerFileEventHandler],
|
|
|
|
[plugins_path, PluginFileEventHandler])
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port']))
|
|
|
|
|
2019-04-11 00:04:46 +00:00
|
|
|
await self.load_plugins()
|
|
|
|
|
2019-03-01 19:41:40 +00:00
|
|
|
async with self.server:
|
|
|
|
await self.server.serve_forever()
|
|
|
|
|
2019-04-11 00:04:46 +00:00
|
|
|
async def load_plugins(self):
|
|
|
|
for plugin_package in self.get_package_modules(Plugins):
|
|
|
|
await self.load_plugin(plugin_package)
|
|
|
|
|
|
|
|
async def load_plugin(self, plugin):
|
|
|
|
plugin_module, plugin_class = plugin
|
|
|
|
|
|
|
|
if plugin_class not in self.server_config['Plugins']:
|
|
|
|
return
|
|
|
|
|
|
|
|
plugin_object = getattr(plugin_module, plugin_class)(self)
|
|
|
|
|
|
|
|
if isinstance(plugin_object, Plugins.IPlugin):
|
|
|
|
self.plugins[plugin_class] = plugin_object
|
|
|
|
|
|
|
|
listeners_from_module(self.xt_listeners, self.xml_listeners, plugin_object)
|
2019-04-22 00:16:41 +00:00
|
|
|
commands_from_plugin(self.commands, plugin_object)
|
2019-04-11 00:04:46 +00:00
|
|
|
|
|
|
|
await plugin_object.ready()
|
|
|
|
else:
|
|
|
|
self.logger.warn('{0} plugin object doesn\'t provide the plugin interface'.format(plugin_class))
|
|
|
|
|
|
|
|
def unload_plugin(self, plugin):
|
|
|
|
plugin_module, plugin_class = plugin
|
|
|
|
|
|
|
|
if plugin_class in self.plugins:
|
|
|
|
plugin_module_path = plugin_module.__file__
|
|
|
|
del self.plugins[plugin_class]
|
|
|
|
|
|
|
|
remove_handlers_by_module(self.xt_listeners, self.xml_listeners, plugin_module_path)
|
|
|
|
|
2019-03-01 19:41:40 +00:00
|
|
|
async def client_connected(self, reader, writer):
|
|
|
|
client_object = self.client_class(self, reader, writer)
|
|
|
|
await client_object.run()
|
|
|
|
|
|
|
|
def load_handler_modules(self, strict_load=None, exclude_load=None):
|
|
|
|
for handler_module in self.get_package_modules(Houdini.Handlers):
|
|
|
|
if not (strict_load and handler_module not in strict_load or exclude_load and handler_module in exclude_load):
|
|
|
|
if handler_module not in sys.modules.keys():
|
|
|
|
module = importlib.import_module(handler_module)
|
|
|
|
listeners_from_module(self.xt_listeners, self.xml_listeners, module)
|
|
|
|
|
2019-04-11 00:04:46 +00:00
|
|
|
self.logger.info('Handler modules loaded')
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
def get_package_modules(self, package):
|
|
|
|
package_modules = []
|
|
|
|
|
|
|
|
for importer, module_name, is_package in pkgutil.iter_modules(package.__path__):
|
2019-04-11 00:04:46 +00:00
|
|
|
full_module_name = '{0}.{1}'.format(package.__name__, module_name)
|
2019-03-01 19:41:40 +00:00
|
|
|
|
|
|
|
if is_package:
|
|
|
|
subpackage_object = importlib.import_module(full_module_name, package=package.__path__)
|
|
|
|
subpackage_object_directory = dir(subpackage_object)
|
|
|
|
|
2019-04-11 00:04:46 +00:00
|
|
|
if Plugins.IPlugin.__name__ in subpackage_object_directory:
|
2019-03-01 19:41:40 +00:00
|
|
|
package_modules.append((subpackage_object, module_name))
|
|
|
|
continue
|
|
|
|
|
|
|
|
sub_package_modules = self.get_package_modules(subpackage_object)
|
|
|
|
|
|
|
|
package_modules = package_modules + sub_package_modules
|
|
|
|
else:
|
|
|
|
package_modules.append(full_module_name)
|
|
|
|
|
|
|
|
return package_modules
|
|
|
|
|
2019-04-22 00:17:01 +00:00
|
|
|
def configure_observers(self, *observer_settings):
|
2019-03-01 19:41:40 +00:00
|
|
|
for observer_path, observer_class in observer_settings:
|
|
|
|
event_observer = Observer()
|
|
|
|
event_observer.schedule(observer_class(self), observer_path, recursive=True)
|
|
|
|
event_observer.start()
|