mirror of
https://github.com/solero/houdini.git
synced 2025-12-29 07:52:41 +00:00
Rename all modules to comply with PEP8
Lowercase with underscores
This commit is contained in:
@@ -1,135 +0,0 @@
|
||||
import inspect
|
||||
import config
|
||||
|
||||
|
||||
from Houdini import Handlers
|
||||
from Houdini import Plugins
|
||||
from Houdini import ConflictResolution
|
||||
|
||||
from Houdini.Converters import _ArgumentDeserializer, _listener
|
||||
|
||||
|
||||
class UnknownCommandException(Exception):
|
||||
"""Raised when a command is executed that doesn't exist"""
|
||||
|
||||
|
||||
class _Command(_ArgumentDeserializer):
|
||||
|
||||
def __init__(self, name, callback, **kwargs):
|
||||
super().__init__(name, callback, **kwargs)
|
||||
|
||||
self.alias = kwargs.get('alias', [])
|
||||
self.parent = kwargs.get('parent', None)
|
||||
|
||||
|
||||
class _CommandGroup(_Command):
|
||||
__slots__ = ['commands']
|
||||
|
||||
def __init__(self, name, callback, **kwargs):
|
||||
super().__init__(name, callback, **kwargs)
|
||||
|
||||
self.commands = {}
|
||||
|
||||
async def __call__(self, p, data):
|
||||
if not data:
|
||||
if self.instance:
|
||||
return await self.callback(self.instance, p)
|
||||
return await self.callback(p)
|
||||
|
||||
await invoke_command_objects(self.commands, p, data)
|
||||
|
||||
def command(self, name=None, **kwargs):
|
||||
return command(name, parent=self, **kwargs)
|
||||
|
||||
def group(self, name=None, **kwargs):
|
||||
return group(name, parent=self, **kwargs)
|
||||
|
||||
|
||||
def command(name=None, **kwargs):
|
||||
return _listener(_Command, name, string_delimiter=config.commands['StringDelimiters'],
|
||||
string_separator=' ', **kwargs)
|
||||
|
||||
|
||||
def group(name=None, **kwargs):
|
||||
return _listener(_CommandGroup, name, string_delimiter=config.commands['StringDelimiters'],
|
||||
string_separator=' ', **kwargs)
|
||||
|
||||
|
||||
cooldown = Handlers.cooldown
|
||||
check = Handlers.check
|
||||
|
||||
player_attribute = Handlers.player_attribute
|
||||
player_data_attribute = Handlers.player_data_attribute
|
||||
player_in_room = Handlers.player_in_room
|
||||
|
||||
|
||||
def is_command(command_object):
|
||||
return issubclass(type(command_object), _Command)
|
||||
|
||||
|
||||
def commands_from_plugin(commands, plugin):
|
||||
command_objects = inspect.getmembers(plugin, is_command)
|
||||
if not isinstance(plugin, Plugins.IPlugin):
|
||||
raise TypeError('Commands can only be loaded from plugins')
|
||||
|
||||
for command_name, command_object in command_objects:
|
||||
command_object.instance = plugin
|
||||
|
||||
if type(command_object.alias) == str:
|
||||
command_object.alias = [command_object.alias]
|
||||
command_object.alias.append(command_object.name)
|
||||
|
||||
parent_commands = commands if command_object.parent is None else command_object.parent.commands
|
||||
|
||||
for name in command_object.alias:
|
||||
if name in parent_commands:
|
||||
conflict_command = parent_commands[name][0]
|
||||
if config.commands['ConflictMode'] == ConflictResolution.Exception:
|
||||
raise NameError('Command name conflict: \'{}\' from plugin \'{}\' '
|
||||
'conflicts with \'{}\' from module \'{}\''
|
||||
.format(name, plugin.__class__.__name__, conflict_command.name,
|
||||
conflict_command.plugin.__class__.__name__))
|
||||
elif config.commands['ConflictMode'] == ConflictResolution.Append:
|
||||
parent_commands[name].append(command_object)
|
||||
elif config.commands['ConflictMode'] == ConflictResolution.Silent:
|
||||
plugin.server.logger.warn('Command \'{}\' from plugin \'{}\' disabled due to conflict with \'{}\''
|
||||
.format(name, plugin.__class__.__name__,
|
||||
conflict_command.plugin.__class__.__name__))
|
||||
else:
|
||||
parent_commands[name] = [command_object]
|
||||
|
||||
|
||||
if type(config.commands['Prefix']) == str:
|
||||
config.commands['Prefix'] = [config.commands['Prefix']]
|
||||
|
||||
|
||||
def has_command_prefix(command_string):
|
||||
for prefix in config.commands['Prefix']:
|
||||
if command_string.startswith(prefix):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_command_prefix(command_string):
|
||||
for prefix in config.commands['Prefix']:
|
||||
if command_string.startswith(prefix):
|
||||
return prefix
|
||||
|
||||
|
||||
async def invoke_command_string(commands, p, command_string):
|
||||
prefix = get_command_prefix(command_string)
|
||||
no_prefix = command_string[len(prefix):]
|
||||
data = no_prefix.split(' ')
|
||||
|
||||
await invoke_command_objects(commands, p, data)
|
||||
|
||||
|
||||
async def invoke_command_objects(commands, p, data):
|
||||
command_identifier = data.pop(0)
|
||||
if command_identifier not in commands:
|
||||
raise UnknownCommandException('Command \'{}\' does not exist'.format(command_identifier))
|
||||
|
||||
command_objects = commands[command_identifier]
|
||||
for command_object in command_objects:
|
||||
await command_object(p, data)
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
|
||||
import asyncio
|
||||
import itertools
|
||||
import inspect
|
||||
|
||||
|
||||
from Houdini.Cooldown import CooldownError
|
||||
|
||||
|
||||
class ChecklistError(Exception):
|
||||
"""Raised when a checklist fails"""
|
||||
|
||||
|
||||
class _ArgumentDeserializer:
|
||||
__slots__ = ['name', 'components', 'callback', 'parent', 'pass_raw', 'cooldown',
|
||||
'checklist', 'instance', 'alias', 'rest_raw', 'string_delimiter',
|
||||
'string_separator', '_signature', '_arguments', '_exception_callback',
|
||||
'_exception_class']
|
||||
|
||||
def __init__(self, name, callback, **kwargs):
|
||||
self.callback = callback
|
||||
|
||||
self.name = callback.__name__ if name is None else name
|
||||
self.cooldown = kwargs.get('cooldown')
|
||||
self.checklist = kwargs.get('checklist', [])
|
||||
self.rest_raw = kwargs.get('rest_raw', False)
|
||||
self.string_delimiter = kwargs.get('string_delimiter', [])
|
||||
self.string_separator = kwargs.get('string_separator', str())
|
||||
|
||||
self.instance = None
|
||||
|
||||
self._signature = list(inspect.signature(self.callback).parameters.values())
|
||||
self._arguments = inspect.getfullargspec(self.callback)
|
||||
|
||||
self._exception_callback = None
|
||||
self._exception_class = Exception
|
||||
|
||||
if self.rest_raw:
|
||||
self._signature = self._signature[:-1]
|
||||
|
||||
def _can_run(self, p):
|
||||
return True if not self.checklist else all(predicate(self, p) for predicate in self.checklist)
|
||||
|
||||
async def _check_cooldown(self, p):
|
||||
if self.cooldown is not None:
|
||||
bucket = self.cooldown.get_bucket(p)
|
||||
if bucket.is_cooling:
|
||||
if self.cooldown.callback is not None:
|
||||
if self.instance:
|
||||
await self.cooldown.callback(self.instance, p)
|
||||
else:
|
||||
await self.cooldown.callback(p)
|
||||
else:
|
||||
raise CooldownError('{} invoked listener during cooldown'.format(p))
|
||||
|
||||
def _check_list(self, p):
|
||||
if not self._can_run(p):
|
||||
raise ChecklistError('Could not invoke listener due to checklist failure')
|
||||
|
||||
def _consume_separated_string(self, ctx):
|
||||
if ctx.argument[0] in self.string_delimiter:
|
||||
while not ctx.argument.endswith(ctx.argument[0]):
|
||||
ctx.argument += self.string_separator + next(ctx.arguments)
|
||||
ctx.argument = ctx.argument[1:-1]
|
||||
|
||||
def error(self, exception_class=Exception):
|
||||
def decorator(exception_callback):
|
||||
self._exception_callback = exception_callback
|
||||
self._exception_class = exception_class
|
||||
return decorator
|
||||
|
||||
async def _deserialize(self, p, data):
|
||||
handler_call_arguments = [self.instance, p] if self.instance is not None else [p]
|
||||
handler_call_keywords = {}
|
||||
|
||||
arguments = itertools.islice(data, len(data) - len(self._arguments.kwonlyargs))
|
||||
keyword_arguments = itertools.islice(data, len(data) - len(self._arguments.kwonlyargs), len(data))
|
||||
|
||||
ctx = _ConverterContext(None, arguments, None, p)
|
||||
for ctx.component in itertools.islice(self._signature, len(handler_call_arguments), len(self._signature)):
|
||||
if ctx.component.annotation is ctx.component.empty and ctx.component.default is not ctx.component.empty:
|
||||
handler_call_arguments.append(ctx.component.default)
|
||||
elif ctx.component.kind == ctx.component.POSITIONAL_OR_KEYWORD:
|
||||
ctx.argument = next(ctx.arguments)
|
||||
converter = get_converter(ctx.component)
|
||||
|
||||
if converter == str:
|
||||
self._consume_separated_string(ctx)
|
||||
|
||||
handler_call_arguments.append(await do_conversion(converter, ctx))
|
||||
elif ctx.component.kind == ctx.component.VAR_POSITIONAL:
|
||||
for argument in ctx.arguments:
|
||||
ctx.argument = argument
|
||||
converter = get_converter(ctx.component)
|
||||
|
||||
if converter == str:
|
||||
self._consume_separated_string(ctx)
|
||||
|
||||
handler_call_arguments.append(await do_conversion(converter, ctx))
|
||||
elif ctx.component.kind == ctx.component.KEYWORD_ONLY:
|
||||
ctx.arguments = keyword_arguments
|
||||
ctx.argument = next(keyword_arguments)
|
||||
converter = get_converter(ctx.component)
|
||||
|
||||
if converter == str:
|
||||
self._consume_separated_string(ctx)
|
||||
|
||||
handler_call_keywords[ctx.component.name] = await do_conversion(converter, ctx)
|
||||
|
||||
if self.rest_raw:
|
||||
handler_call_arguments.append(list(ctx.arguments))
|
||||
|
||||
return handler_call_arguments, handler_call_keywords
|
||||
|
||||
async def __call__(self, p, data):
|
||||
try:
|
||||
handler_call_arguments, handler_call_keywords = await self._deserialize(p, data)
|
||||
|
||||
return await self.callback(*handler_call_arguments, **handler_call_keywords)
|
||||
except Exception as e:
|
||||
if self._exception_callback and isinstance(e, self._exception_class):
|
||||
if self.instance:
|
||||
await self._exception_callback(self.instance, e)
|
||||
else:
|
||||
await self._exception_callback(e)
|
||||
else:
|
||||
raise e
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.__name__())
|
||||
|
||||
def __name__(self):
|
||||
return "{}.{}".format(self.callback.__module__, self.callback.__name__)
|
||||
|
||||
|
||||
def _listener(cls, name, **kwargs):
|
||||
def decorator(callback):
|
||||
if not asyncio.iscoroutinefunction(callback):
|
||||
raise TypeError('All listeners must be a coroutine.')
|
||||
|
||||
try:
|
||||
cooldown_object = callback.__cooldown
|
||||
del callback.__cooldown
|
||||
except AttributeError:
|
||||
cooldown_object = None
|
||||
|
||||
try:
|
||||
checklist = callback.__checks
|
||||
del callback.__checks
|
||||
except AttributeError:
|
||||
checklist = []
|
||||
|
||||
listener_object = cls(name, callback, cooldown=cooldown_object, checklist=checklist, **kwargs)
|
||||
return listener_object
|
||||
return decorator
|
||||
|
||||
|
||||
class IConverter(ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def description(self):
|
||||
"""A short description of the purpose of the converter"""
|
||||
|
||||
@abstractmethod
|
||||
async def convert(self, ctx):
|
||||
"""The actual converter implementation"""
|
||||
|
||||
|
||||
class CredentialsConverter(IConverter):
|
||||
|
||||
description = """Used for obtaining login credentials from XML login data"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
username = ctx.argument[0][0].text
|
||||
password = ctx.argument[0][1].text
|
||||
return username, password
|
||||
|
||||
|
||||
class VersionChkConverter(IConverter):
|
||||
|
||||
description = """Used for checking the verChk version number"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
return int(ctx.argument[0].get('v'))
|
||||
|
||||
|
||||
class ConnectedPenguinConverter(IConverter):
|
||||
|
||||
description = """Converts a penguin ID into a live penguin instance
|
||||
or none if the player is offline"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
penguin_id = int(ctx.argument)
|
||||
if penguin_id in ctx.p.server.penguins_by_id:
|
||||
return ctx.p.server.penguins_by_id[penguin_id]
|
||||
return None
|
||||
|
||||
|
||||
class ConnectedIglooConverter(IConverter):
|
||||
|
||||
description = """Converts a penguin ID into a live igloo instance or
|
||||
none if it's not available"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
igloo_id = int(ctx.argument)
|
||||
if igloo_id in ctx.p.server.igloo_map:
|
||||
return ctx.p.server.igloo_map[igloo_id]
|
||||
return None
|
||||
|
||||
|
||||
class RoomConverter(IConverter):
|
||||
|
||||
description = """Converts a room ID into a Houdini.Data.Room instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
room_id = int(ctx.argument)
|
||||
if room_id in ctx.p.server.rooms:
|
||||
return await ctx.p.server.rooms.get(room_id)
|
||||
return None
|
||||
|
||||
|
||||
class ItemConverter(IConverter):
|
||||
|
||||
description = """Converts an item ID into a Houdini.Data.Item instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
item_id = int(ctx.argument)
|
||||
if item_id in ctx.p.server.items:
|
||||
return await ctx.p.server.items.get(item_id)
|
||||
return None
|
||||
|
||||
|
||||
class IglooConverter(IConverter):
|
||||
|
||||
description = """Converts an igloo ID into a Houdini.Data.Igloo instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
igloo_id = int(ctx.argument)
|
||||
if igloo_id in ctx.p.server.igloos:
|
||||
return await ctx.p.server.igloos.get(igloo_id)
|
||||
return None
|
||||
|
||||
|
||||
class FurnitureConverter(IConverter):
|
||||
|
||||
description = """Converts a furniture ID into a Houdini.Data.Furniture instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
furniture_id = int(ctx.argument)
|
||||
if furniture_id in ctx.p.server.furniture:
|
||||
return await ctx.p.server.furniture.get(furniture_id)
|
||||
return None
|
||||
|
||||
|
||||
class FlooringConverter(IConverter):
|
||||
|
||||
description = """Converts a flooring ID into a Houdini.Data.Flooring instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
flooring_id = int(ctx.argument)
|
||||
if flooring_id in ctx.p.server.flooring:
|
||||
return await ctx.p.server.flooring.get(flooring_id)
|
||||
return None
|
||||
|
||||
|
||||
class StampConverter(IConverter):
|
||||
|
||||
description = """Converts a stamp ID into a Houdini.Data.Stamp instance"""
|
||||
|
||||
async def convert(self, ctx):
|
||||
stamp_id = int(ctx.argument)
|
||||
if stamp_id in ctx.p.server.stamps:
|
||||
return await ctx.p.server.stamps.get(stamp_id)
|
||||
return None
|
||||
|
||||
|
||||
class SeparatorConverter(IConverter):
|
||||
|
||||
__slots__ = ['separator', 'mapper']
|
||||
|
||||
description = """Converts strings separated by char into a list of type"""
|
||||
|
||||
def __init__(self, separator='|', mapper=int):
|
||||
self.separator = separator
|
||||
self.mapper = mapper
|
||||
|
||||
async def convert(self, ctx):
|
||||
return map(self.mapper, ctx.argument.split(self.separator))
|
||||
|
||||
|
||||
class UnionConverter(IConverter):
|
||||
|
||||
__slots__ = ['types']
|
||||
|
||||
description = """Converts union type into argument"""
|
||||
|
||||
def __init__(self, *types, skip_none=False):
|
||||
self.types = types
|
||||
self.skip_none = skip_none
|
||||
|
||||
async def convert(self, ctx):
|
||||
for converter in self.types:
|
||||
try:
|
||||
result = await do_conversion(converter, ctx)
|
||||
if not self.skip_none or result is not None:
|
||||
return result
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
|
||||
class GreedyConverter(IConverter):
|
||||
|
||||
__slots__ = ['target']
|
||||
|
||||
description = """Converts until it can't any longer"""
|
||||
|
||||
def __init__(self, target=int):
|
||||
self.target = target
|
||||
|
||||
async def convert(self, ctx):
|
||||
converted = []
|
||||
try:
|
||||
converted.append(await do_conversion(self.target, ctx))
|
||||
for ctx.argument in ctx.arguments:
|
||||
converted.append(await do_conversion(self.target, ctx))
|
||||
except ValueError:
|
||||
return converted
|
||||
return converted
|
||||
|
||||
|
||||
class OptionalConverter(IConverter):
|
||||
|
||||
__slots__ = ['target']
|
||||
|
||||
description = """Tries to convert but ignores if it can't"""
|
||||
|
||||
def __init__(self, target=int):
|
||||
self.target = target
|
||||
|
||||
async def convert(self, ctx):
|
||||
try:
|
||||
return await do_conversion(self.target, ctx)
|
||||
except ValueError:
|
||||
return ctx.component.default
|
||||
|
||||
|
||||
class _ConverterContext:
|
||||
|
||||
__slots__ = ['component', 'arguments', 'argument', 'p']
|
||||
|
||||
def __init__(self, component, arguments, argument, p):
|
||||
self.component = component
|
||||
self.arguments = arguments
|
||||
self.argument = argument
|
||||
self.p = p
|
||||
|
||||
|
||||
def get_converter(component):
|
||||
if component.annotation is component.empty:
|
||||
return str
|
||||
return component.annotation
|
||||
|
||||
|
||||
async def do_conversion(converter, ctx):
|
||||
if issubclass(converter, IConverter) and not isinstance(converter, IConverter):
|
||||
converter = converter()
|
||||
if isinstance(converter, IConverter):
|
||||
if asyncio.iscoroutinefunction(converter.convert):
|
||||
return await converter.convert(ctx)
|
||||
return converter.convert(ctx)
|
||||
return converter(ctx.argument)
|
||||
@@ -1,87 +0,0 @@
|
||||
import enum
|
||||
import time
|
||||
|
||||
|
||||
class CooldownError(Exception):
|
||||
"""Raised when packets are sent whilst a cooldown is active"""
|
||||
pass
|
||||
|
||||
|
||||
class BucketType(enum.Enum):
|
||||
Default = 1
|
||||
Penguin = 1
|
||||
Server = 2
|
||||
|
||||
|
||||
class _Cooldown:
|
||||
|
||||
__slots__ = ['rate', 'per', 'bucket_type', 'last',
|
||||
'_window', '_tokens']
|
||||
|
||||
def __init__(self, per, rate, bucket_type):
|
||||
self.per = float(per)
|
||||
self.rate = int(rate)
|
||||
self.bucket_type = bucket_type
|
||||
self.last = 0.0
|
||||
|
||||
self._window = 0.0
|
||||
self._tokens = self.rate
|
||||
|
||||
@property
|
||||
def is_cooling(self):
|
||||
current = time.time()
|
||||
self.last = current
|
||||
|
||||
if self._tokens == self.rate:
|
||||
self._window = current
|
||||
|
||||
if current > self._window + self.per:
|
||||
self._tokens = self.rate
|
||||
self._window = current
|
||||
|
||||
if self._tokens == 0:
|
||||
return self.per - (current - self._window)
|
||||
|
||||
self._tokens -= 1
|
||||
if self._tokens == 0:
|
||||
self._window = current
|
||||
|
||||
def reset(self):
|
||||
self._tokens = self.rate
|
||||
self.last = 0.0
|
||||
|
||||
def copy(self):
|
||||
return _Cooldown(self.per, self.rate, self.bucket_type)
|
||||
|
||||
|
||||
class _CooldownMapping:
|
||||
|
||||
__slots__ = ['_cooldown', '_cache', 'callback']
|
||||
|
||||
def __init__(self, callback, cooldown_object):
|
||||
self._cooldown = cooldown_object
|
||||
|
||||
self.callback = callback
|
||||
|
||||
self._cache = {}
|
||||
|
||||
def _get_bucket_key(self, p):
|
||||
if self._cooldown.bucket_type == BucketType.Default:
|
||||
return p
|
||||
return p.server
|
||||
|
||||
def _verify_cache_integrity(self):
|
||||
current = time.time()
|
||||
self._cache = {cache_key: bucket for cache_key, bucket in
|
||||
self._cache.items() if current < bucket.last + bucket.per}
|
||||
|
||||
def get_bucket(self, p):
|
||||
self._verify_cache_integrity()
|
||||
cache_key = self._get_bucket_key(p)
|
||||
if cache_key not in self._cache:
|
||||
bucket = self._cooldown.copy()
|
||||
self._cache[cache_key] = bucket
|
||||
else:
|
||||
bucket = self._cache[cache_key]
|
||||
return bucket
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import hashlib
|
||||
import secrets
|
||||
import string
|
||||
|
||||
_alphabet = string.ascii_letters + string.digits
|
||||
|
||||
|
||||
class Crypto:
|
||||
|
||||
@staticmethod
|
||||
def hash(undigested):
|
||||
if type(undigested) == str:
|
||||
undigested = undigested.encode('utf-8')
|
||||
return hashlib.md5(undigested).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def generate_random_key():
|
||||
return ''.join(secrets.choice(_alphabet) for _ in range(16))
|
||||
|
||||
@staticmethod
|
||||
def encrypt_password(password, digest=True):
|
||||
if digest:
|
||||
password = Crypto.hash(password)
|
||||
|
||||
swapped_hash = password[16:32] + password[0:16]
|
||||
return swapped_hash
|
||||
|
||||
@staticmethod
|
||||
def get_login_hash(password, rndk):
|
||||
key = Crypto.encrypt_password(password, False)
|
||||
key += rndk
|
||||
key += 'Y(02.>\'H}t":E1'
|
||||
|
||||
login_hash = Crypto.encrypt_password(key)
|
||||
|
||||
return login_hash
|
||||
@@ -1,47 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class BuddyList(db.Model):
|
||||
__tablename__ = 'buddy_list'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
BuddyID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False, index=True)
|
||||
BestBuddy = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
t_buddy_request = db.Table(
|
||||
'buddy_request', db,
|
||||
db.Column('PenguinID', db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('RequesterID', db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
|
||||
t_ignore_list = db.Table(
|
||||
'ignore_list', db,
|
||||
db.Column('PenguinID', db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('IgnoreID', db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False, index=True)
|
||||
)
|
||||
|
||||
|
||||
class Character(db.Model):
|
||||
__tablename__ = 'character'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(30), nullable=False)
|
||||
GiftID = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
StampID = db.Column(db.ForeignKey('stamp.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
|
||||
|
||||
t_character_buddy = db.Table(
|
||||
'character_buddy', db,
|
||||
db.Column('PenguinID', db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('CharacterID', db.ForeignKey('character.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class DanceSong(db.Model):
|
||||
__tablename__ = 'dance_song'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(30), nullable=False)
|
||||
SongLength = db.Column(db.Integer, nullable=False, server_default=db.text("100000"))
|
||||
MillisPerBar = db.Column(db.Integer, nullable=False, server_default=db.text("2000"))
|
||||
@@ -1,96 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinIgloo, PenguinLocation, PenguinFurniture
|
||||
|
||||
|
||||
class Flooring(db.Model):
|
||||
__tablename__ = 'flooring'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50))
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class Furniture(db.Model):
|
||||
__tablename__ = 'furniture'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
Type = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
Sort = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Bait = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
MaxQuantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
|
||||
|
||||
class Igloo(db.Model):
|
||||
__tablename__ = 'igloo'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
Cost = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class IglooFurniture(db.Model):
|
||||
__tablename__ = 'igloo_furniture'
|
||||
|
||||
IglooID = db.Column(db.ForeignKey('penguin_igloo_room.ID', ondelete='CASCADE', onupdate='CASCADE'),
|
||||
primary_key=True, nullable=False, index=True)
|
||||
FurnitureID = db.Column(db.ForeignKey('furniture.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
X = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("0"))
|
||||
Y = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("0"))
|
||||
Frame = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("0"))
|
||||
Rotation = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("0"))
|
||||
|
||||
|
||||
class IglooLike(db.Model):
|
||||
__tablename__ = 'igloo_like'
|
||||
|
||||
IglooID = db.Column(db.ForeignKey('penguin_igloo_room.ID', ondelete='CASCADE', onupdate='CASCADE'),
|
||||
primary_key=True, nullable=False)
|
||||
OwnerID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
PlayerID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Count = db.Column(db.SmallInteger, nullable=False)
|
||||
Date = db.Column(db.Date, nullable=False, server_default=db.text("now()"))
|
||||
|
||||
|
||||
class Location(db.Model):
|
||||
__tablename__ = 'location'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class IglooCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Igloo, key='ID', inventory_model=PenguinIgloo,
|
||||
inventory_id=inventory_id)
|
||||
|
||||
|
||||
class LocationCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Location, key='ID', inventory_model=PenguinLocation,
|
||||
inventory_id=inventory_id)
|
||||
|
||||
|
||||
class FurnitureCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Furniture, key='ID', inventory_model=PenguinFurniture,
|
||||
inventory_id=inventory_id)
|
||||
|
||||
|
||||
class FlooringCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(model=Flooring, key='ID')
|
||||
@@ -1,24 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinItem
|
||||
|
||||
|
||||
class Item(db.Model):
|
||||
__tablename__ = 'item'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50))
|
||||
Type = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Bait = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
EPF = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Tour = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ReleaseDate = db.Column(db.Date, nullable=False, server_default=db.text("now()"))
|
||||
|
||||
|
||||
class ItemCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, model=Item, key='ID', inventory_model=PenguinItem, inventory_id=None):
|
||||
super().__init__(model=model, key=key, inventory_model=inventory_model,
|
||||
inventory_id=inventory_id)
|
||||
@@ -1,18 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinPostcard
|
||||
|
||||
|
||||
class Postcard(db.Model):
|
||||
__tablename__ = 'postcard'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("10"))
|
||||
Enabled = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PostcardCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Postcard, key='ID', inventory_model=PenguinPostcard,
|
||||
inventory_id=inventory_id)
|
||||
@@ -1,37 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class Ban(db.Model):
|
||||
__tablename__ = 'ban'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Issued = db.Column(db.DateTime, primary_key=True, nullable=False, server_default=db.text("now()"))
|
||||
Expires = db.Column(db.DateTime, primary_key=True, nullable=False, server_default=db.text("now()"))
|
||||
ModeratorID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), index=True)
|
||||
Reason = db.Column(db.SmallInteger, nullable=False)
|
||||
Comment = db.Column(db.Text)
|
||||
|
||||
|
||||
class Warning(db.Model):
|
||||
__tablename__ = 'warning'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Issued = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||
Expires = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||
Type = db.Column(db.SmallInteger, nullable=False)
|
||||
Comment = db.Column(db.Text, nullable=False)
|
||||
|
||||
|
||||
class Report(db.Model):
|
||||
__tablename__ = 'report'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('\"report_ID_seq\"'::regclass)"))
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
ReporterID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
ReportType = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Date = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
ServerID = db.Column(db.Integer, nullable=False)
|
||||
RoomID = db.Column(db.ForeignKey('room.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class PenguinTrack(db.Model):
|
||||
__tablename__ = 'penguin_track'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True,
|
||||
server_default=db.text("nextval('\"penguin_track_ID_seq\"'::regclass)"))
|
||||
Name = db.Column(db.String(12), nullable=False, server_default=db.text("''::character varying"))
|
||||
OwnerID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Sharing = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Pattern = db.Column(db.Text, nullable=False)
|
||||
|
||||
|
||||
class TrackLike(db.Model):
|
||||
__tablename__ = 'track_like'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
TrackID = db.Column(db.ForeignKey('penguin_track.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False, index=True)
|
||||
Date = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||
@@ -1,22 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinCard
|
||||
|
||||
|
||||
class Card(db.Model):
|
||||
__tablename__ = 'card'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
SetID = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
PowerID = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Element = db.Column(db.CHAR(1), nullable=False, server_default=db.text("'s'::bpchar"))
|
||||
Color = db.Column(db.CHAR(1), nullable=False, server_default=db.text("'b'::bpchar"))
|
||||
Value = db.Column(db.SmallInteger, nullable=False, server_default=db.text("2"))
|
||||
Description = db.Column(db.String(255), nullable=False, server_default=db.text("''::character varying"))
|
||||
|
||||
|
||||
class CardCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Card, key='ID', inventory_model=PenguinCard,
|
||||
inventory_id=inventory_id)
|
||||
@@ -1,242 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class Penguin(db.Model):
|
||||
__tablename__ = 'penguin'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('\"penguin_ID_seq\"'::regclass)"))
|
||||
Username = db.Column(db.String(12), nullable=False, unique=True)
|
||||
Nickname = db.Column(db.String(30), nullable=False)
|
||||
Password = db.Column(db.CHAR(60), nullable=False)
|
||||
Email = db.Column(db.String(255), nullable=False, index=True)
|
||||
RegistrationDate = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
Active = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
SafeChat = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
LastPaycheck = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
MinutesPlayed = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Moderator = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Character = db.Column(db.ForeignKey('character.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Igloo = db.Column(db.ForeignKey('penguin_igloo_room.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Coins = db.Column(db.Integer, nullable=False, server_default=db.text("500"))
|
||||
Color = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Head = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Face = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Neck = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Body = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Hand = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Feet = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Photo = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Flag = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Permaban = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
BookModified = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
BookColor = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
BookHighlight = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
BookPattern = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
BookIcon = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
AgentStatus = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
FieldOpStatus = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
CareerMedals = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
AgentMedals = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
LastFieldOp = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
NinjaRank = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
NinjaProgress = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
FireNinjaRank = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
FireNinjaProgress = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
WaterNinjaRank = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
WaterNinjaProgress = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
NinjaMatchesWon = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
FireMatchesWon = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
WaterMatchesWon = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
RainbowAdoptability = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
HasDug = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Nuggets = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
TimerActive = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
TimerStart = db.Column(db.Time, nullable=False, server_default=db.text("'00:00:00'::time without time zone"))
|
||||
TimerEnd = db.Column(db.Time, nullable=False, server_default=db.text("'23:59:59'::time without time zone"))
|
||||
ApprovalEn = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ApprovalPt = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ApprovalFr = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ApprovalEs = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ApprovalDe = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
ApprovalRu = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionEn = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionPt = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionFr = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionEs = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionDe = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
RejectionRu = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
@property
|
||||
def approval(self):
|
||||
return int('{}{}0{}{}{}{}'.format(self.ApprovalRu * 1, self.ApprovalDe * 1, self.ApprovalEs * 1,
|
||||
self.ApprovalFr * 1, self.ApprovalPt * 1, self.ApprovalEn * 1), 2)
|
||||
|
||||
|
||||
class ActivationKey(db.Model):
|
||||
__tablename__ = 'activation_key'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
ActivationKey = db.Column(db.CHAR(255), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class PuffleQuest(db.Model):
|
||||
__tablename__ = 'puffle_quest'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
TaskID = db.Column(db.SmallInteger, primary_key=True, nullable=False)
|
||||
CompletionDate = db.Column(db.DateTime)
|
||||
ItemCollected = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
CoinsCollected = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PenguinIgloo(db.Model):
|
||||
__tablename__ = 'penguin_igloo'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
IglooID = db.Column(db.ForeignKey('igloo.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class PenguinItem(db.Model):
|
||||
__tablename__ = 'penguin_item'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'),
|
||||
primary_key=True, nullable=False)
|
||||
ItemID = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'),
|
||||
primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class PenguinLocation(db.Model):
|
||||
__tablename__ = 'penguin_location'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
LocationID = db.Column(db.ForeignKey('location.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class PenguinStamp(db.Model):
|
||||
__tablename__ = 'penguin_stamp'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
StampID = db.Column(db.ForeignKey('stamp.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Recent = db.Column(db.Boolean, nullable=False, server_default=db.text("true"))
|
||||
|
||||
|
||||
class PenguinRedemption(db.Model):
|
||||
__tablename__ = 'penguin_redemption'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
CodeID = db.Column(db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False, index=True)
|
||||
BookID = db.Column(db.ForeignKey('redemption_book.ID', ondelete='CASCADE', onupdate='CASCADE'), index=True)
|
||||
|
||||
|
||||
class PenguinMembership(db.Model):
|
||||
__tablename__ = 'penguin_membership'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Start = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||
End = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class PenguinPostcard(db.Model):
|
||||
__tablename__ = 'penguin_postcard'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True,
|
||||
server_default=db.text("nextval('\"penguin_postcard_ID_seq\"'::regclass)"))
|
||||
SenderID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), index=True)
|
||||
RecipientID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False,
|
||||
index=True)
|
||||
PostcardID = db.Column(db.ForeignKey('postcard.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
SendDate = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
Details = db.Column(db.String(255), nullable=False, server_default=db.text("''::character varying"))
|
||||
HasRead = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PenguinIglooRoom(db.Model):
|
||||
__tablename__ = 'penguin_igloo_room'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True,
|
||||
server_default=db.text("nextval('\"penguin_igloo_room_ID_seq\"'::regclass)"))
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Type = db.Column(db.ForeignKey('igloo.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Flooring = db.Column(db.ForeignKey('flooring.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Music = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Location = db.Column(db.ForeignKey('location.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Locked = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PenguinLaunchGame(db.Model):
|
||||
__tablename__ = 'penguin_launch_game'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Level = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("0"))
|
||||
PuffleOs = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
BestTime = db.Column(db.SmallInteger, nullable=False, server_default=db.text("600"))
|
||||
Turbo = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PenguinCard(db.Model):
|
||||
__tablename__ = 'penguin_card'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False, index=True)
|
||||
CardID = db.Column(db.ForeignKey('card.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Quantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
|
||||
|
||||
class PenguinFurniture(db.Model):
|
||||
__tablename__ = 'penguin_furniture'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
FurnitureID = db.Column(db.ForeignKey('furniture.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Quantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
|
||||
|
||||
class PenguinPuffle(db.Model):
|
||||
__tablename__ = 'penguin_puffle'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True,
|
||||
server_default=db.text("nextval('\"penguin_puffle_ID_seq\"'::regclass)"))
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Name = db.Column(db.String(16), nullable=False)
|
||||
AdoptionDate = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
Type = db.Column(db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Food = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
Play = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
Rest = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
Clean = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
Walking = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
Hat = db.Column(db.ForeignKey('puffle_item.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Backyard = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
HasDug = db.Column(db.Boolean, server_default=db.text("false"))
|
||||
|
||||
|
||||
class PenguinPuffleItem(db.Model):
|
||||
__tablename__ = 'penguin_puffle_item'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
ItemID = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||
Quantity = db.Column(db.SmallInteger, nullable=False)
|
||||
|
||||
|
||||
class Login(db.Model):
|
||||
__tablename__ = 'login'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True, server_default=db.text("nextval('\"login_ID_seq\"'::regclass)"))
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Date = db.Column(db.DateTime, nullable=False, server_default=db.text("now()"))
|
||||
IPAddress = db.Column(db.CHAR(255), nullable=False)
|
||||
@@ -1,74 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinPuffle, PenguinPuffleItem
|
||||
|
||||
|
||||
class Puffle(db.Model):
|
||||
__tablename__ = 'puffle'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
ParentID = db.Column(db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Name = db.Column(db.String(50), nullable=False, server_default=db.text("''::character varying"))
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
FavouriteFood = db.Column(db.ForeignKey('puffle_item.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
RunawayPostcard = db.Column(db.ForeignKey('postcard.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
MaxFood = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
MaxRest = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
MaxClean = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
|
||||
|
||||
|
||||
class PuffleItem(db.Model):
|
||||
__tablename__ = 'puffle_item'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
ParentID = db.Column(db.ForeignKey('puffle_item.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Name = db.Column(db.String(50), nullable=False, server_default=db.text("''::character varying"))
|
||||
Type = db.Column(db.String(10), nullable=False, server_default=db.text("'care'::character varying"))
|
||||
PlayExternal = db.Column(db.String(10), nullable=False, server_default=db.text("'none'::character varying"))
|
||||
Cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Quantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
FoodEffect = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
RestEffect = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
PlayEffect = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
CleanEffect = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
|
||||
|
||||
class PuffleTreasureFurniture(db.Model):
|
||||
__tablename__ = 'puffle_treasure_furniture'
|
||||
|
||||
PuffleID = db.Column(db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
FurnitureID = db.Column(db.ForeignKey('furniture.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class PuffleTreasureItem(db.Model):
|
||||
__tablename__ = 'puffle_treasure_item'
|
||||
|
||||
PuffleID = db.Column(db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
ItemID = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class PuffleTreasurePuffleItem(db.Model):
|
||||
__tablename__ = 'puffle_treasure_puffle_item'
|
||||
|
||||
PuffleID = db.Column(db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
PuffleItemID = db.Column(db.ForeignKey('puffle_item.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class PuffleCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Puffle, key='ID', inventory_model=PenguinPuffle,
|
||||
inventory_id=inventory_id)
|
||||
|
||||
|
||||
class PuffleItemCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=PuffleItem, key='ID', inventory_model=PenguinPuffleItem,
|
||||
inventory_id=inventory_id)
|
||||
@@ -1,95 +0,0 @@
|
||||
from Houdini.Data import db
|
||||
|
||||
|
||||
class RedemptionBook(db.Model):
|
||||
__tablename__ = 'redemption_book'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(255), nullable=False)
|
||||
|
||||
|
||||
class RedemptionBookWord(db.Model):
|
||||
__tablename__ = 'redemption_book_word'
|
||||
|
||||
BookID = db.Column(db.ForeignKey('redemption_book.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Page = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("1"))
|
||||
Line = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("1"))
|
||||
WordNumber = db.Column(db.SmallInteger, primary_key=True, nullable=False, server_default=db.text("1"))
|
||||
Answer = db.Column(db.String(20), nullable=False)
|
||||
|
||||
|
||||
class RedemptionCode(db.Model):
|
||||
__tablename__ = 'redemption_code'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True,
|
||||
server_default=db.text("nextval('\"redemption_code_ID_seq\"'::regclass)"))
|
||||
Code = db.Column(db.String(16), nullable=False, unique=True)
|
||||
Type = db.Column(db.String(8), nullable=False, server_default=db.text("'BLANKET'::character varying"))
|
||||
Coins = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
|
||||
Expires = db.Column(db.DateTime)
|
||||
|
||||
|
||||
t_redemption_award_card = db.Table(
|
||||
'redemption_award_card', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('CardID', db.ForeignKey('card.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_flooring = db.Table(
|
||||
'redemption_award_flooring', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('FlooringID', db.ForeignKey('flooring.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_furniture = db.Table(
|
||||
'redemption_award_furniture', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('FurnitureID', db.ForeignKey('furniture.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_igloo = db.Table(
|
||||
'redemption_award_igloo', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('IglooID', db.ForeignKey('igloo.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_item = db.Table(
|
||||
'redemption_award_item', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('ItemID', db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_location = db.Table(
|
||||
'redemption_award_location', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('LocationID', db.ForeignKey('location.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_puffle = db.Table(
|
||||
'redemption_award_puffle', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('PuffleID', db.ForeignKey('puffle.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
|
||||
t_redemption_award_puffle_item = db.Table(
|
||||
'redemption_award_puffle_item', db,
|
||||
db.Column('CodeID', db.ForeignKey('redemption_code.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False),
|
||||
db.Column('PuffleItemID', db.ForeignKey('puffle_item.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
)
|
||||
@@ -1,176 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
|
||||
|
||||
class Room(db.Model):
|
||||
__tablename__ = 'room'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
InternalID = db.Column(db.Integer, nullable=False, unique=True,
|
||||
server_default=db.text("nextval('\"room_InternalID_seq\"'::regclass)"))
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
MaxUsers = db.Column(db.SmallInteger, nullable=False, server_default=db.text("80"))
|
||||
RequiredItem = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
Game = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Blackhole = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Spawn = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
StampGroup = db.Column(db.ForeignKey('stamp_group.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.penguins = []
|
||||
|
||||
self.tables = {}
|
||||
self.waddles = {}
|
||||
|
||||
async def add_penguin(self, p):
|
||||
if p.room:
|
||||
await p.room.remove_penguin(p)
|
||||
self.penguins.append(p)
|
||||
|
||||
p.room = self
|
||||
|
||||
await p.send_xt('jr', await self.get_string())
|
||||
await self.send_xt('ap', await p.server.penguin_string_compiler.compile(p))
|
||||
|
||||
async def remove_penguin(self, p):
|
||||
await self.send_xt('rp', p.data.ID)
|
||||
|
||||
self.penguins.remove(p)
|
||||
p.room = None
|
||||
|
||||
async def get_string(self):
|
||||
return '%'.join([await p.server.penguin_string_compiler.compile(p) for p in self.penguins])
|
||||
|
||||
async def send_xt(self, *data):
|
||||
for penguin in self.penguins:
|
||||
await penguin.send_xt(*data)
|
||||
|
||||
|
||||
class RoomTable(db.Model):
|
||||
__tablename__ = 'room_table'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||
RoomID = db.Column(db.ForeignKey('room.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Game = db.Column(db.String(20), nullable=False)
|
||||
|
||||
GameClassMapping = {
|
||||
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.penguins = []
|
||||
self.room = None
|
||||
|
||||
async def add(self, p):
|
||||
self.penguins.append(p)
|
||||
|
||||
seat_id = len(self.penguins) - 1
|
||||
|
||||
await p.send_xt("jt", self.ID, seat_id + 1)
|
||||
await p.room.send_xt("ut", self.ID, len(self.penguins))
|
||||
p.table = self
|
||||
|
||||
return seat_id
|
||||
|
||||
async def remove(self, p):
|
||||
self.penguins.remove(p)
|
||||
|
||||
await p.send_xt("lt")
|
||||
await p.room.send_xt("ut", self.ID, len(self.penguins))
|
||||
p.table = None
|
||||
|
||||
async def reset(self):
|
||||
for penguin in self.penguins:
|
||||
penguin.table = None
|
||||
|
||||
self.penguins = []
|
||||
await self.room.send_xt("ut", self.ID, 0)
|
||||
|
||||
def get_seat_id(self, p):
|
||||
return self.penguins.index(p)
|
||||
|
||||
def get_string(self):
|
||||
if len(self.penguins) == 0:
|
||||
return str()
|
||||
elif len(self.penguins) == 1:
|
||||
player_one, = self.penguins
|
||||
return "%".join([player_one.Nickname, str(), self.game.get_string()])
|
||||
player_one, player_two = self.penguins[:2]
|
||||
if len(self.penguins) == 2:
|
||||
return "%".join([player_one.Nickname, player_two.Nickname, self.game.get_string()])
|
||||
return "%".join([player_one.Nickname, player_two.Nickname, self.game.get_string(), "1"])
|
||||
|
||||
async def send_xt(self, *data):
|
||||
for penguin in self.penguins:
|
||||
await penguin.send_xt(*data)
|
||||
|
||||
|
||||
class RoomWaddle(db.Model):
|
||||
__tablename__ = 'room_waddle'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||
RoomID = db.Column(db.ForeignKey('room.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
Seats = db.Column(db.SmallInteger, nullable=False, server_default=db.text("2"))
|
||||
Game = db.Column(db.String(20), nullable=False)
|
||||
|
||||
GameClassMapping = {
|
||||
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.penguins = []
|
||||
|
||||
async def add(self, p):
|
||||
if not self.penguins:
|
||||
self.penguins = [None] * self.Seats
|
||||
|
||||
seat_id = self.penguins.index(None)
|
||||
self.penguins[seat_id] = p
|
||||
await p.send_xt("jw", seat_id)
|
||||
await p.room.send_xt("uw", self.ID, seat_id, p.Nickname)
|
||||
|
||||
p.waddle = self
|
||||
|
||||
if self.penguins.count(None) == 0:
|
||||
await self.reset()
|
||||
|
||||
async def remove(self, p):
|
||||
seat_id = self.get_seat_id(p)
|
||||
self.penguins[seat_id] = None
|
||||
await self.room.send_xt("uw", self.ID, seat_id)
|
||||
|
||||
p.waddle = None
|
||||
|
||||
async def reset(self):
|
||||
for seat_id, penguin in enumerate(self.penguins):
|
||||
if penguin:
|
||||
self.penguins[seat_id] = None
|
||||
await penguin.room.send_xt("uw", self.ID, seat_id)
|
||||
|
||||
def get_seat_id(self, p):
|
||||
return self.penguins.index(p)
|
||||
|
||||
|
||||
class RoomCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(model=Room, key='ID')
|
||||
|
||||
def get_spawn_rooms(self):
|
||||
return [room for room in self.values() if room.Spawn]
|
||||
|
||||
async def setup_tables(self):
|
||||
async with self._db.transaction():
|
||||
async for table in RoomTable.query.gino.iterate():
|
||||
self[table.RoomID].tables[table.ID] = table
|
||||
|
||||
async def setup_waddles(self):
|
||||
async with self._db.transaction():
|
||||
async for waddle in RoomWaddle.query.gino.iterate():
|
||||
self[waddle.RoomID].waddles[waddle.ID] = waddle
|
||||
@@ -1,43 +0,0 @@
|
||||
from Houdini.Data import db, BaseCrumbsCollection
|
||||
from Houdini.Data.Penguin import PenguinStamp
|
||||
|
||||
|
||||
class Stamp(db.Model):
|
||||
__tablename__ = 'stamp'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
GroupID = db.Column(db.ForeignKey('stamp_group.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
Member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||
Rank = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
|
||||
Description = db.Column(db.String(255), nullable=False, server_default=db.text("''::character varying"))
|
||||
|
||||
|
||||
class StampGroup(db.Model):
|
||||
__tablename__ = 'stamp_group'
|
||||
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
Name = db.Column(db.String(50), nullable=False)
|
||||
ParentID = db.Column(db.ForeignKey('stamp_group.ID', ondelete='CASCADE', onupdate='CASCADE'))
|
||||
|
||||
|
||||
class CoverStamp(db.Model):
|
||||
__tablename__ = 'cover_stamp'
|
||||
|
||||
PenguinID = db.Column(db.ForeignKey('penguin.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
StampID = db.Column(db.ForeignKey('stamp.ID', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||
nullable=False)
|
||||
ItemID = db.Column(db.ForeignKey('item.ID', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
|
||||
X = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Y = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Type = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Rotation = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
Depth = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
|
||||
|
||||
|
||||
class StampCrumbsCollection(BaseCrumbsCollection):
|
||||
|
||||
def __init__(self, inventory_id=None):
|
||||
super().__init__(model=Stamp, key='ID', inventory_model=PenguinStamp,
|
||||
inventory_id=inventory_id)
|
||||
@@ -1,46 +0,0 @@
|
||||
from gino import Gino
|
||||
|
||||
db = Gino()
|
||||
|
||||
|
||||
class BaseCrumbsCollection(dict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__()
|
||||
|
||||
self._db = db
|
||||
self._model = kwargs.get('model')
|
||||
self._key = kwargs.get('key')
|
||||
self._inventory_model = kwargs.get('inventory_model')
|
||||
self._inventory_id = kwargs.get('inventory_id')
|
||||
|
||||
self._is_inventory = self._inventory_model is not None and self._inventory_id is not None
|
||||
|
||||
async def get(self, k):
|
||||
if self._is_inventory:
|
||||
return self[k]
|
||||
try:
|
||||
return self[k]
|
||||
except KeyError as e:
|
||||
result = await self._model.query.where(getattr(self._model, self._key) == k).gino.first()
|
||||
if result:
|
||||
self[k] = result
|
||||
return result
|
||||
raise e
|
||||
|
||||
async def __collect(self):
|
||||
query = self._model.load(parent=self._inventory_model).where(
|
||||
self._inventory_model.PenguinID == self._inventory_id
|
||||
) if self._is_inventory else self._model.query
|
||||
|
||||
async with db.transaction():
|
||||
collected = query.gino.iterate()
|
||||
self.update(
|
||||
{getattr(model_instance, self._key): model_instance async for model_instance in collected}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_collection(cls, *args, **kwargs):
|
||||
cc = cls(*args, **kwargs)
|
||||
await cc.__collect()
|
||||
return cc
|
||||
@@ -1,81 +0,0 @@
|
||||
import sys
|
||||
import importlib
|
||||
import copy
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from Houdini.Handlers import listeners_from_module, remove_handlers_by_module
|
||||
from Houdini.Events import evaluate_handler_file_event
|
||||
|
||||
|
||||
class HandlerFileEventHandler(FileSystemEventHandler):
|
||||
|
||||
def __init__(self, server):
|
||||
self.logger = server.logger
|
||||
self.server = server
|
||||
|
||||
def on_created(self, event):
|
||||
handler_module_details = evaluate_handler_file_event(event)
|
||||
|
||||
if not handler_module_details:
|
||||
return
|
||||
|
||||
handler_module_path, handler_module = handler_module_details
|
||||
|
||||
if '__init__.py' in handler_module_path:
|
||||
return
|
||||
|
||||
self.logger.debug('New handler module detected %s', handler_module)
|
||||
|
||||
try:
|
||||
module = importlib.import_module(handler_module)
|
||||
listeners_from_module(self.server.xt_listeners, self.server.xml_listeners, module)
|
||||
except Exception as import_error:
|
||||
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, handler_module)
|
||||
|
||||
def on_deleted(self, event):
|
||||
handler_module_details = evaluate_handler_file_event(event)
|
||||
|
||||
if not handler_module_details:
|
||||
return
|
||||
|
||||
handler_module_path, handler_module = handler_module_details
|
||||
|
||||
if handler_module not in sys.modules:
|
||||
return
|
||||
|
||||
self.logger.debug('Deleting listeners registered by %s...', handler_module)
|
||||
|
||||
remove_handlers_by_module(self.server.xt_listeners, self.server.xml_listeners, handler_module_path)
|
||||
|
||||
def on_modified(self, event):
|
||||
handler_module_details = evaluate_handler_file_event(event)
|
||||
|
||||
if not handler_module_details:
|
||||
return
|
||||
|
||||
handler_module_path, handler_module = handler_module_details
|
||||
|
||||
if handler_module not in sys.modules:
|
||||
return False
|
||||
|
||||
self.logger.info('Reloading %s', handler_module)
|
||||
|
||||
xt_listeners, xml_listeners = copy.copy(self.server.xt_listeners), copy.copy(self.server.xml_listeners)
|
||||
|
||||
remove_handlers_by_module(self.server.xt_listeners, self.server.xml_listeners, handler_module_path)
|
||||
|
||||
handler_module_object = sys.modules[handler_module]
|
||||
|
||||
try:
|
||||
module = importlib.reload(handler_module_object)
|
||||
listeners_from_module(self.server.xt_listeners, self.server.xml_listeners, module)
|
||||
|
||||
self.logger.info('Successfully reloaded %s!', handler_module)
|
||||
except Exception as rebuild_error:
|
||||
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, handler_module)
|
||||
self.logger.info('Restoring handler references...')
|
||||
|
||||
self.server.xt_listeners = xt_listeners
|
||||
self.server.xml_listeners = xml_listeners
|
||||
|
||||
self.logger.info('Handler references restored. Phew!')
|
||||
@@ -1,86 +0,0 @@
|
||||
import sys
|
||||
import importlib
|
||||
import os.path
|
||||
import copy
|
||||
import asyncio
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from Houdini.Events import evaluate_handler_file_event
|
||||
|
||||
|
||||
class PluginFileEventHandler(FileSystemEventHandler):
|
||||
|
||||
def __init__(self, server):
|
||||
self.logger = server.logger
|
||||
self.server = server
|
||||
|
||||
def on_created(self, event):
|
||||
plugin_module_details = evaluate_handler_file_event(event)
|
||||
|
||||
if not plugin_module_details:
|
||||
return
|
||||
|
||||
plugin_module_path, plugin_module = plugin_module_details
|
||||
|
||||
self.logger.debug('New handler module detected %s', plugin_module)
|
||||
|
||||
try:
|
||||
plugin_module_object = importlib.import_module(plugin_module)
|
||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
||||
|
||||
asyncio.run(self.server.load_plugin((plugin_module_object, plugin_class)))
|
||||
|
||||
self.logger.info('New plugin \'%s\' has been loaded.' % plugin_class)
|
||||
except Exception as import_error:
|
||||
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, plugin_module)
|
||||
|
||||
def on_deleted(self, event):
|
||||
plugin_module_path = event.src_path[2:]
|
||||
|
||||
plugin_module = plugin_module_path.replace(os.path.pathsep, ".")
|
||||
|
||||
if plugin_module not in sys.modules:
|
||||
return
|
||||
|
||||
self.logger.debug('Deleting listeners registered by %s.', plugin_module)
|
||||
|
||||
plugin_module_object = sys.modules[plugin_module]
|
||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
||||
|
||||
self.server.unload_plugin((plugin_module_object, plugin_class))
|
||||
|
||||
def on_modified(self, event):
|
||||
plugin_module_details = evaluate_handler_file_event(event)
|
||||
|
||||
if not plugin_module_details:
|
||||
return
|
||||
|
||||
plugin_module_path, plugin_module = plugin_module_details
|
||||
|
||||
if plugin_module not in sys.modules:
|
||||
return
|
||||
|
||||
self.logger.info('Reloading %s', plugin_module)
|
||||
|
||||
plugin_module_object = sys.modules[plugin_module]
|
||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
||||
|
||||
xt_listeners, xml_listeners = copy.copy(self.server.xt_listeners), copy.copy(self.server.xml_listeners)
|
||||
|
||||
self.server.unload_plugin((plugin_module_object, plugin_class))
|
||||
|
||||
try:
|
||||
new_plugin_module = importlib.reload(plugin_module_object)
|
||||
asyncio.run(self.server.load_plugin((new_plugin_module, plugin_class)))
|
||||
|
||||
self.logger.info('Successfully reloaded %s!', plugin_module)
|
||||
except LookupError as lookup_error:
|
||||
self.logger.warn('Did not reload plugin \'%s\': %s.', plugin_class, lookup_error)
|
||||
except Exception as rebuild_error:
|
||||
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, plugin_module)
|
||||
self.logger.info('Restoring handler references...')
|
||||
|
||||
self.server.xt_handlers = xt_listeners
|
||||
self.server.xml_handlers = xml_listeners
|
||||
|
||||
self.logger.info('Restored handler references. Phew!')
|
||||
@@ -1,38 +0,0 @@
|
||||
import os
|
||||
import copy
|
||||
|
||||
|
||||
def evaluate_handler_file_event(handler_file_event):
|
||||
# Ignore all directory events
|
||||
if handler_file_event.is_directory:
|
||||
return False
|
||||
|
||||
handler_module_path = handler_file_event.src_path[2:]
|
||||
|
||||
# Ignore non-Python files
|
||||
if handler_module_path[-3:] != ".py":
|
||||
return False
|
||||
|
||||
handler_module = handler_module_path.replace(os.path.sep, ".")[:-3]
|
||||
|
||||
return handler_module_path, handler_module
|
||||
|
||||
|
||||
def evaluate_plugin_file_event(plugin_file_event):
|
||||
# Ignore all directory events
|
||||
if plugin_file_event.is_directory:
|
||||
return False
|
||||
|
||||
handler_module_path = plugin_file_event.src_path[2:]
|
||||
|
||||
# Ignore non-Python files
|
||||
if handler_module_path[-3:] != ".py":
|
||||
return False
|
||||
|
||||
# Remove file extension and replace path separator with dots. Then make like a banana.. and split.
|
||||
handler_module_tokens = handler_module_path.replace(os.path.sep, ".")[:-3].split(".")
|
||||
|
||||
if handler_module_tokens.pop() == "__init__":
|
||||
return handler_module_path, ".".join(handler_module_tokens)
|
||||
|
||||
return False
|
||||
@@ -1,120 +0,0 @@
|
||||
from Houdini import Handlers
|
||||
from Houdini.Handlers import XMLPacket, Login
|
||||
from Houdini.Converters import CredentialsConverter
|
||||
from Houdini.Data.Penguin import Penguin
|
||||
from Houdini.Data.Buddy import BuddyList
|
||||
from Houdini.Data.Moderator import Ban
|
||||
from Houdini.Crypto import Crypto
|
||||
|
||||
import asyncio
|
||||
import bcrypt
|
||||
import time
|
||||
import os
|
||||
import config
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@Handlers.handler(XMLPacket('login'))
|
||||
@Handlers.allow_once()
|
||||
async def handle_login(p, credentials: CredentialsConverter):
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
login_timestamp = time.time()
|
||||
username, password = credentials
|
||||
p.logger.info('{} is logging in!'.format(username))
|
||||
|
||||
data = await Penguin.query.where(Penguin.Username == username).gino.first()
|
||||
|
||||
if data is None:
|
||||
p.logger.info('{} failed to login: penguin does not exist')
|
||||
await p.send_error_and_disconnect(100)
|
||||
|
||||
password_correct = await loop.run_in_executor(None, bcrypt.checkpw,
|
||||
password.encode('utf-8'), data.Password.encode('utf-8'))
|
||||
|
||||
ip_addr = p.peer_name[0]
|
||||
|
||||
if not password_correct:
|
||||
p.logger.info('{} failed to login: incorrect password'.format(username))
|
||||
|
||||
if ip_addr in p.server.login_attempts:
|
||||
last_failed_attempt, failure_count = p.server.login_attempts[ip_addr]
|
||||
|
||||
failure_count = 1 if login_timestamp - last_failed_attempt >= p.server.server_config['LoginFailureTimer'] \
|
||||
else failure_count + 1
|
||||
|
||||
p.server.login_attempts[ip_addr] = [login_timestamp, failure_count]
|
||||
|
||||
if failure_count >= p.server.server_config['LoginFailureLimit']:
|
||||
return await p.send_error_and_disconnect(150)
|
||||
else:
|
||||
p.server.login_attempts[ip_addr] = [login_timestamp, 1]
|
||||
|
||||
return await p.send_error_and_disconnect(101)
|
||||
|
||||
if ip_addr in p.server.login_attempts:
|
||||
previous_attempt, failure_count = p.server.login_attempts[ip_addr]
|
||||
|
||||
max_attempts_exceeded = failure_count >= p.server.server_config['LoginFailureLimit']
|
||||
timer_surpassed = (login_timestamp - previous_attempt) > p.server.server_config['LoginFailureTimer']
|
||||
|
||||
if max_attempts_exceeded and not timer_surpassed:
|
||||
return await p.send_error_and_disconnect(150)
|
||||
else:
|
||||
del p.server.login_attempts[ip_addr]
|
||||
|
||||
if not data.Active:
|
||||
return await p.send_error_and_disconnect(900)
|
||||
|
||||
if data.Permaban:
|
||||
return await p.send_error_and_disconnect(603)
|
||||
|
||||
active_ban = await Ban.query.where(Ban.PenguinID == data.ID and Ban.Expires >= datetime.now()).gino.first()
|
||||
|
||||
if active_ban is not None:
|
||||
hours_left = round((active_ban.Expires - datetime.now()).total_seconds() / 60 / 60)
|
||||
|
||||
if hours_left == 0:
|
||||
return await p.send_error_and_disconnect(602)
|
||||
else:
|
||||
await p.send_error_and_disconnect(601, hours_left)
|
||||
|
||||
p.logger.info('{} has logged in successfully'.format(username))
|
||||
|
||||
random_key = Crypto.generate_random_key()
|
||||
login_key = Crypto.hash(random_key[::-1])
|
||||
confirmation_hash = Crypto.hash(os.urandom(24))
|
||||
|
||||
await p.server.redis.setex('{}.lkey'.format(data.ID), p.server.server_config['KeyTTL'], login_key)
|
||||
await p.server.redis.setex('{}.ckey'.format(data.ID), p.server.server_config['KeyTTL'], confirmation_hash)
|
||||
|
||||
buddy_worlds = []
|
||||
world_populations = []
|
||||
|
||||
servers_config = config.servers
|
||||
|
||||
for server_name, server_config in servers_config.items():
|
||||
if server_config['World']:
|
||||
server_population = await p.server.redis.get('{}.population'.format(server_name))
|
||||
server_population = int(server_population) / (server_config['Capacity'] / 6) if server_population else 0
|
||||
|
||||
server_players = await p.server.redis.smembers('{}.players'.format(server_name))
|
||||
|
||||
world_populations.append('{},{}'.format(server_config['Id'], server_population))
|
||||
|
||||
if not len(server_players) > 0:
|
||||
p.logger.debug('Skipping buddy iteration for {}'.format(server_name))
|
||||
continue
|
||||
|
||||
buddies = await BuddyList.select('BuddyID').where(BuddyList.PenguinID == data.ID).gino.all()
|
||||
for buddy_id in buddies:
|
||||
if str(buddy_id) in server_players:
|
||||
buddy_worlds.append(server_config['Id'])
|
||||
break
|
||||
|
||||
raw_login_data = '|'.join([str(data.ID), str(data.ID), data.Username, login_key, str(data.approval), '1'])
|
||||
await p.send_xt('l', raw_login_data, confirmation_hash, 'friendsKey', '|'.join(world_populations), data.Email)
|
||||
|
||||
handle_version_check = Login.handle_version_check
|
||||
handle_random_key = Login.handle_random_key
|
||||
@@ -1,19 +0,0 @@
|
||||
from Houdini import Handlers
|
||||
from Houdini.Handlers import XMLPacket
|
||||
from Houdini.Converters import VersionChkConverter
|
||||
|
||||
|
||||
@Handlers.handler(XMLPacket('verChk'))
|
||||
@Handlers.allow_once()
|
||||
async def handle_version_check(p, version: VersionChkConverter):
|
||||
if not version == 153:
|
||||
await p.send_xml({'body': {'action': 'apiKO', 'r': '0'}})
|
||||
await p.close()
|
||||
else:
|
||||
await p.send_xml({'body': {'action': 'apiOK', 'r': '0'}})
|
||||
|
||||
|
||||
@Handlers.handler(XMLPacket('rndK'))
|
||||
@Handlers.allow_once()
|
||||
async def handle_random_key(p, data):
|
||||
await p.send_xml({'body': {'action': 'rndK', 'r': '-1'}, 'k': 'houdini'})
|
||||
@@ -1,214 +0,0 @@
|
||||
import inspect
|
||||
import enum
|
||||
import os
|
||||
import itertools
|
||||
from types import FunctionType
|
||||
|
||||
from Houdini.Converters import _listener, _ArgumentDeserializer, get_converter, do_conversion, _ConverterContext
|
||||
|
||||
from Houdini.Cooldown import _Cooldown, _CooldownMapping, BucketType
|
||||
from Houdini import Plugins
|
||||
|
||||
|
||||
class AuthorityError(Exception):
|
||||
"""Raised when a packet is received but user has not yet authenticated"""
|
||||
|
||||
|
||||
class _Packet:
|
||||
__slots__ = ['id']
|
||||
|
||||
def __init__(self):
|
||||
self.id = None
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.id)
|
||||
|
||||
|
||||
class XTPacket(_Packet):
|
||||
def __init__(self, *packet_id):
|
||||
super().__init__()
|
||||
self.id = '#'.join(packet_id)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.id)
|
||||
|
||||
|
||||
class XMLPacket(_Packet):
|
||||
def __init__(self, packet_id):
|
||||
super().__init__()
|
||||
self.id = packet_id
|
||||
|
||||
|
||||
class Priority(enum.Enum):
|
||||
Override = 3
|
||||
High = 2
|
||||
Low = 1
|
||||
|
||||
|
||||
class _Listener(_ArgumentDeserializer):
|
||||
|
||||
__slots__ = ['priority', 'packet', 'overrides']
|
||||
|
||||
def __init__(self, packet, callback, **kwargs):
|
||||
super().__init__(packet.id, callback, **kwargs)
|
||||
self.packet = packet
|
||||
|
||||
self.priority = kwargs.get('priority', Priority.Low)
|
||||
self.overrides = kwargs.get('overrides', [])
|
||||
|
||||
if type(self.overrides) is not list:
|
||||
self.overrides = [self.overrides]
|
||||
|
||||
|
||||
class _XTListener(_Listener):
|
||||
|
||||
__slots__ = ['pre_login']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.pre_login = kwargs.get('pre_login')
|
||||
|
||||
async def __call__(self, p, packet_data):
|
||||
if not self.pre_login and not p.joined_world:
|
||||
await p.close()
|
||||
raise AuthorityError('{} tried sending XT packet before authentication!'.format(p))
|
||||
|
||||
await super()._check_cooldown(p)
|
||||
super()._check_list(p)
|
||||
|
||||
await super().__call__(p, packet_data)
|
||||
|
||||
|
||||
class _XMLListener(_Listener):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
async def __call__(self, p, packet_data):
|
||||
await super()._check_cooldown(p)
|
||||
super()._check_list(p)
|
||||
|
||||
handler_call_arguments = [self.instance, p] if self.instance is not None else [p]
|
||||
|
||||
ctx = _ConverterContext(None, None, packet_data, p)
|
||||
for ctx.component in itertools.islice(self._signature, len(handler_call_arguments), len(self._signature)):
|
||||
if ctx.component.default is not ctx.component.empty:
|
||||
handler_call_arguments.append(ctx.component.default)
|
||||
elif ctx.component.kind == ctx.component.POSITIONAL_OR_KEYWORD:
|
||||
converter = get_converter(ctx.component)
|
||||
|
||||
handler_call_arguments.append(await do_conversion(converter, ctx))
|
||||
return await self.callback(*handler_call_arguments)
|
||||
|
||||
|
||||
def get_relative_function_path(function_obj):
|
||||
abs_function_file = inspect.getfile(function_obj)
|
||||
rel_function_file = os.path.relpath(abs_function_file)
|
||||
|
||||
return rel_function_file
|
||||
|
||||
|
||||
def handler(packet, **kwargs):
|
||||
if not issubclass(type(packet), _Packet):
|
||||
raise TypeError('All handlers can only listen for either XMLPacket or XTPacket.')
|
||||
|
||||
listener_class = _XTListener if isinstance(packet, XTPacket) else _XMLListener
|
||||
return _listener(listener_class, packet, **kwargs)
|
||||
|
||||
|
||||
def listener_exists(xt_listeners, xml_listeners, packet):
|
||||
listener_collection = xt_listeners if isinstance(packet, XTPacket) else xml_listeners
|
||||
return packet in listener_collection
|
||||
|
||||
|
||||
def is_listener(listener):
|
||||
return issubclass(type(listener), _Listener)
|
||||
|
||||
|
||||
def listeners_from_module(xt_listeners, xml_listeners, module):
|
||||
listener_objects = inspect.getmembers(module, is_listener)
|
||||
for listener_name, listener_object in listener_objects:
|
||||
if isinstance(module, Plugins.IPlugin):
|
||||
listener_object.instance = module
|
||||
|
||||
listener_collection = xt_listeners if type(listener_object) == _XTListener else xml_listeners
|
||||
if listener_object.packet not in listener_collection:
|
||||
listener_collection[listener_object.packet] = []
|
||||
|
||||
if listener_object not in listener_collection[listener_object.packet]:
|
||||
if listener_object.priority == Priority.High:
|
||||
listener_collection[listener_object.packet].insert(0, listener_object)
|
||||
elif listener_object.priority == Priority.Override:
|
||||
listener_collection[listener_object.packet] = [listener_object]
|
||||
else:
|
||||
listener_collection[listener_object.packet].append(listener_object)
|
||||
|
||||
for listener_name, listener_object in listener_objects:
|
||||
listener_collection = xt_listeners if type(listener_object) == _XTListener else xml_listeners
|
||||
for override in listener_object.overrides:
|
||||
listener_collection[override.packet].remove(override)
|
||||
|
||||
|
||||
def remove_handlers_by_module(xt_listeners, xml_listeners, handler_module_path):
|
||||
def remove_handlers(remove_handler_items):
|
||||
for handler_id, handler_listeners in remove_handler_items:
|
||||
for handler_listener in handler_listeners:
|
||||
handler_file = get_relative_function_path(handler_listener.callback)
|
||||
if handler_file == handler_module_path:
|
||||
handler_listeners.remove(handler_listener)
|
||||
remove_handlers(xt_listeners.items())
|
||||
remove_handlers(xml_listeners.items())
|
||||
|
||||
|
||||
def cooldown(per=1.0, rate=1, bucket_type=BucketType.Default, callback=None):
|
||||
def decorator(handler_function):
|
||||
handler_function.__cooldown = _CooldownMapping(callback, _Cooldown(per, rate, bucket_type))
|
||||
return handler_function
|
||||
return decorator
|
||||
|
||||
|
||||
def check(predicate):
|
||||
def decorator(handler_function):
|
||||
if not hasattr(handler_function, 'checks'):
|
||||
handler_function.__checks = []
|
||||
|
||||
if not type(predicate) == FunctionType:
|
||||
raise TypeError('All handler checks must be a function')
|
||||
|
||||
handler_function.__checks.append(predicate)
|
||||
return handler_function
|
||||
return decorator
|
||||
|
||||
|
||||
def allow_once():
|
||||
def check_for_packet(listener, p):
|
||||
return listener.packet not in p.received_packets
|
||||
return check(check_for_packet)
|
||||
|
||||
|
||||
def player_attribute(**attrs):
|
||||
def check_for_attributes(_, p):
|
||||
for attr, value in attrs.items():
|
||||
if not getattr(p, attr) == value:
|
||||
return False
|
||||
return True
|
||||
return check(check_for_attributes)
|
||||
|
||||
|
||||
def player_data_attribute(**attrs):
|
||||
def check_for_attributes(_, p):
|
||||
for attr, value in attrs.items():
|
||||
if not getattr(p.data, attr) == value:
|
||||
return False
|
||||
return True
|
||||
return check(check_for_attributes)
|
||||
|
||||
|
||||
def player_in_room(*room_ids):
|
||||
def check_room_id(_, p):
|
||||
return p.room.ID in room_ids
|
||||
return check(check_room_id)
|
||||
@@ -1,288 +0,0 @@
|
||||
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
|
||||
|
||||
from Houdini.Data import db
|
||||
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
|
||||
|
||||
try:
|
||||
import uvloop
|
||||
uvloop.install()
|
||||
except ImportError:
|
||||
uvloop = None
|
||||
|
||||
import Houdini.Handlers
|
||||
from Houdini.Handlers import listeners_from_module, remove_handlers_by_module
|
||||
from Houdini.Events.HandlerFileEvent import HandlerFileEventHandler
|
||||
from Houdini.Events.PluginFileEvent import PluginFileEventHandler
|
||||
|
||||
from Houdini.Commands import commands_from_plugin
|
||||
|
||||
import Houdini.Plugins as Plugins
|
||||
|
||||
|
||||
class HoudiniFactory:
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.server = None
|
||||
self.redis = None
|
||||
self.config = None
|
||||
self.cache = None
|
||||
self.db = db
|
||||
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
|
||||
self.anonymous_penguin_string_compiler = None
|
||||
|
||||
self.penguins_by_id = {}
|
||||
self.penguins_by_username = {}
|
||||
|
||||
self.login_attempts = {}
|
||||
|
||||
self.xt_listeners, self.xml_listeners = {}, {}
|
||||
self.commands = {}
|
||||
self.plugins = {}
|
||||
|
||||
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
|
||||
|
||||
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':
|
||||
self.logger = Logger(name='Houdini')
|
||||
universal_handler = AsyncTimedRotatingFileHandler(
|
||||
filename=self.server_config['Logging']['General'],
|
||||
backup_count=3,
|
||||
when=RolloverInterval.HOURS
|
||||
)
|
||||
error_handler = AsyncFileHandler(filename=self.server_config['Logging']['Errors'])
|
||||
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)
|
||||
self.logger.addHandler(error_handler)
|
||||
|
||||
level = logging.getLevelName(self.server_config['Logging']['Level'])
|
||||
self.logger.setLevel(level)
|
||||
|
||||
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']))
|
||||
|
||||
self.logger.info('Houdini module instantiated')
|
||||
|
||||
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
||||
self.config.redis['Address'], self.config.redis['Port']),
|
||||
minsize=5, maxsize=10)
|
||||
|
||||
if self.server_config['World']:
|
||||
await self.redis.delete('{}.players'.format(self.server_name))
|
||||
await self.redis.delete('{}.population'.format(self.server_name))
|
||||
|
||||
self.cache = SimpleMemoryCache(namespace='houdini', ttl=self.server_config['CacheExpiry'])
|
||||
|
||||
self.client_class = Penguin
|
||||
self.penguin_string_compiler = PenguinStringCompiler()
|
||||
self.anonymous_penguin_string_compiler = PenguinStringCompiler()
|
||||
|
||||
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
|
||||
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_penguin_string_compiler)
|
||||
|
||||
self.load_handler_modules(exclude_load='Houdini.Handlers.Login.Login')
|
||||
self.logger.info('World server started')
|
||||
else:
|
||||
self.load_handler_modules('Houdini.Handlers.Login.Login')
|
||||
self.logger.info('Login server started')
|
||||
|
||||
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)))
|
||||
|
||||
handlers_path = './Houdini{}Handlers'.format(os.path.sep)
|
||||
plugins_path = './Houdini{}Plugins'.format(os.path.sep)
|
||||
self.configure_observers([handlers_path, HandlerFileEventHandler],
|
||||
[plugins_path, PluginFileEventHandler])
|
||||
|
||||
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port']))
|
||||
|
||||
await self.load_plugins()
|
||||
|
||||
async with self.server:
|
||||
await self.server.serve_forever()
|
||||
|
||||
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)
|
||||
commands_from_plugin(self.commands, plugin_object)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
self.logger.info('Handler modules loaded')
|
||||
|
||||
def get_package_modules(self, package):
|
||||
package_modules = []
|
||||
|
||||
for importer, module_name, is_package in pkgutil.iter_modules(package.__path__):
|
||||
full_module_name = '{0}.{1}'.format(package.__name__, module_name)
|
||||
|
||||
if is_package:
|
||||
subpackage_object = importlib.import_module(full_module_name, package=package.__path__)
|
||||
subpackage_object_directory = dir(subpackage_object)
|
||||
|
||||
if Plugins.IPlugin.__name__ in subpackage_object_directory:
|
||||
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
|
||||
|
||||
def configure_observers(self, *observer_settings):
|
||||
for observer_path, observer_class in observer_settings:
|
||||
event_observer = Observer()
|
||||
event_observer.schedule(observer_class(self), observer_path, recursive=True)
|
||||
event_observer.start()
|
||||
@@ -1,61 +0,0 @@
|
||||
from Houdini.Spheniscidae import Spheniscidae
|
||||
|
||||
|
||||
class Penguin(Spheniscidae):
|
||||
|
||||
__slots__ = ['x', 'y', 'room', 'waddle', 'table', 'data', 'member',
|
||||
'membership_days', 'avatar', 'walking_puffle']
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
|
||||
self.x, self.y = (0, 0)
|
||||
self.room = None
|
||||
self.waddle = None
|
||||
self.table = None
|
||||
|
||||
self.data = None
|
||||
|
||||
self.member = None
|
||||
self.membership_days = 0
|
||||
|
||||
self.avatar = None
|
||||
|
||||
self.walking_puffle = None
|
||||
|
||||
self.logger.debug('New penguin created')
|
||||
|
||||
@property
|
||||
def party_state(self):
|
||||
return str()
|
||||
|
||||
@property
|
||||
def puffle_state(self):
|
||||
return str()
|
||||
|
||||
@property
|
||||
def penguin_state(self):
|
||||
return str()
|
||||
|
||||
async def add_inventory(self, item):
|
||||
pass
|
||||
|
||||
async def add_igloo(self, igloo):
|
||||
pass
|
||||
|
||||
async def add_furniture(self, furniture):
|
||||
pass
|
||||
|
||||
async def add_flooring(self, flooring):
|
||||
pass
|
||||
|
||||
async def add_buddy(self, buddy):
|
||||
pass
|
||||
|
||||
async def add_inbox(self, postcard):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
if self.data is not None:
|
||||
return '<Penguin ID=\'{}\' Username=\'{}\'>'.format(self.data.ID, self.data.Username)
|
||||
return super().__repr__()
|
||||
@@ -1,33 +0,0 @@
|
||||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
class IPlugin(ABC):
|
||||
"""
|
||||
Plugin interface which all plugins *must* implement.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def author(self):
|
||||
"""The plugin's author which is usually a nickname and an e-mail address."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def version(self):
|
||||
"""The version of the plugin."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def description(self):
|
||||
"""Short summary of the plugin's intended purpose."""
|
||||
|
||||
@abstractmethod
|
||||
async def ready(self):
|
||||
"""
|
||||
Called when the plugin is ready to function.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
@@ -1,162 +0,0 @@
|
||||
from Houdini import Handlers
|
||||
from Houdini.Handlers import XMLPacket, XTPacket
|
||||
|
||||
from asyncio import IncompleteReadError
|
||||
|
||||
import defusedxml.cElementTree as Et
|
||||
from xml.etree.cElementTree import Element, SubElement, tostring
|
||||
|
||||
|
||||
class Spheniscidae:
|
||||
|
||||
__slots__ = ['__reader', '__writer', 'server', 'logger',
|
||||
'peer_name', 'received_packets', 'joined_world']
|
||||
|
||||
Delimiter = b'\x00'
|
||||
|
||||
def __init__(self, server, reader, writer):
|
||||
self.__reader = reader
|
||||
self.__writer = writer
|
||||
|
||||
self.server = server
|
||||
self.logger = server.logger
|
||||
|
||||
self.peer_name = writer.get_extra_info('peername')
|
||||
self.server.peers_by_ip[self.peer_name] = self
|
||||
|
||||
self.joined_world = False
|
||||
|
||||
self.received_packets = set()
|
||||
|
||||
async def send_error_and_disconnect(self, error, *args):
|
||||
await self.send_xt('e', error, *args)
|
||||
await self.close()
|
||||
|
||||
async def send_error(self, error, *args):
|
||||
await self.send_xt('e', error, *args)
|
||||
|
||||
async def send_policy_file(self):
|
||||
await self.send_line('<cross-domain-policy><allow-access-from domain="*" to-ports="{}" /></cross-domain-policy>'
|
||||
.format(self.server.server_config['Port']))
|
||||
await self.close()
|
||||
|
||||
async def send_xt(self, *data):
|
||||
data = list(data)
|
||||
|
||||
handler_id = data.pop(0)
|
||||
internal_id = -1
|
||||
|
||||
mapped_data = map(str, data)
|
||||
|
||||
xt_data = '%'.join(mapped_data)
|
||||
line = '%xt%{0}%{1}%{2}%'.format(handler_id, internal_id, xt_data)
|
||||
await self.send_line(line)
|
||||
|
||||
async def send_xml(self, xml_dict):
|
||||
data_root = Element('msg')
|
||||
data_root.set('t', 'sys')
|
||||
|
||||
sub_element_parent = data_root
|
||||
for sub_element, sub_element_attribute in xml_dict.items():
|
||||
sub_element_object = SubElement(sub_element_parent, sub_element)
|
||||
|
||||
if type(xml_dict[sub_element]) is dict:
|
||||
for sub_element_attribute_key, sub_element_attribute_value in xml_dict[sub_element].items():
|
||||
sub_element_object.set(sub_element_attribute_key, sub_element_attribute_value)
|
||||
else:
|
||||
sub_element_object.text = xml_dict[sub_element]
|
||||
|
||||
sub_element_parent = sub_element_object
|
||||
|
||||
xml_data = tostring(data_root)
|
||||
await self.send_line(xml_data.decode('utf-8'))
|
||||
|
||||
async def send_line(self, data):
|
||||
self.logger.debug('Outgoing data: %s', data)
|
||||
self.__writer.write(data.encode('utf-8') + Spheniscidae.Delimiter)
|
||||
|
||||
async def close(self):
|
||||
self.__writer.close()
|
||||
|
||||
async def __handle_xt_data(self, data):
|
||||
self.logger.debug('Received XT data: %s', data)
|
||||
parsed_data = data.split('%')[1:-1]
|
||||
|
||||
packet_id = parsed_data[2]
|
||||
packet = XTPacket(packet_id)
|
||||
|
||||
if Handlers.listener_exists(self.server.xt_listeners, self.server.xml_listeners, packet):
|
||||
xt_listeners = self.server.xt_listeners[packet]
|
||||
packet_data = parsed_data[4:]
|
||||
|
||||
for listener in xt_listeners:
|
||||
await listener(self, packet_data)
|
||||
self.received_packets.add(packet)
|
||||
else:
|
||||
self.logger.debug('Handler for %s doesn\'t exist!', packet_id)
|
||||
|
||||
async def __handle_xml_data(self, data):
|
||||
self.logger.debug('Received XML data: %s', data)
|
||||
|
||||
element_tree = Et.fromstring(data)
|
||||
|
||||
if element_tree.tag == 'policy-file-request':
|
||||
await self.send_policy_file()
|
||||
|
||||
elif element_tree.tag == 'msg':
|
||||
self.logger.debug('Received valid XML data')
|
||||
|
||||
try:
|
||||
body_tag = element_tree[0]
|
||||
action = body_tag.get('action')
|
||||
packet = XMLPacket(action)
|
||||
|
||||
if Handlers.listener_exists(self.server.xt_listeners, self.server.xml_listeners, packet):
|
||||
xml_listeners = self.server.xml_listeners[packet]
|
||||
|
||||
for listener in xml_listeners:
|
||||
await listener(self, body_tag)
|
||||
|
||||
self.received_packets.add(packet)
|
||||
else:
|
||||
self.logger.warn('Packet did not contain a valid action attribute!')
|
||||
|
||||
except IndexError:
|
||||
self.logger.warn('Received invalid XML data (didn\'t contain a body tag)')
|
||||
else:
|
||||
self.logger.warn('Received invalid XML data!')
|
||||
|
||||
async def __client_connected(self):
|
||||
self.logger.info('Client %s connected', self.peer_name)
|
||||
|
||||
async def __client_disconnected(self):
|
||||
del self.server.peers_by_ip[self.peer_name]
|
||||
|
||||
self.logger.info('Client %s disconnected', self.peer_name)
|
||||
|
||||
async def __data_received(self, data):
|
||||
data = data.decode()[:-1]
|
||||
if data.startswith('<'):
|
||||
await self.__handle_xml_data(data)
|
||||
else:
|
||||
await self.__handle_xt_data(data)
|
||||
|
||||
async def run(self):
|
||||
await self.__client_connected()
|
||||
while not self.__writer.is_closing():
|
||||
try:
|
||||
data = await self.__reader.readuntil(
|
||||
separator=Spheniscidae.Delimiter)
|
||||
if data:
|
||||
await self.__data_received(data)
|
||||
else:
|
||||
self.__writer.close()
|
||||
await self.__writer.drain()
|
||||
except IncompleteReadError:
|
||||
self.__writer.close()
|
||||
except ConnectionResetError:
|
||||
self.__writer.close()
|
||||
await self.__client_disconnected()
|
||||
|
||||
def __repr__(self):
|
||||
return '<Spheniscidae {}>'.format(self.peer_name)
|
||||
@@ -1,86 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
from aiocache import cached
|
||||
from types import FunctionType
|
||||
import asyncio
|
||||
import enum
|
||||
|
||||
|
||||
class ConflictResolution(enum.Enum):
|
||||
Silent = 0
|
||||
Append = 1
|
||||
Exception = 2
|
||||
|
||||
|
||||
class PenguinStringCompiler(OrderedDict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __setitem__(self, key, compiler_method):
|
||||
assert type(compiler_method) == FunctionType
|
||||
super().__setitem__(key, compiler_method)
|
||||
|
||||
@cached(namespace='houdini')
|
||||
async def compile(self, p):
|
||||
compiler_method_results = []
|
||||
|
||||
for compiler_method in self.values():
|
||||
if asyncio.iscoroutinefunction(compiler_method):
|
||||
compiler_method_result = await compiler_method(p)
|
||||
else:
|
||||
compiler_method_result = compiler_method(p)
|
||||
compiler_method_results.append(str(compiler_method_result))
|
||||
|
||||
compiler_result = '|'.join(compiler_method_results)
|
||||
return compiler_result
|
||||
|
||||
@classmethod
|
||||
def attribute_by_name(cls, attribute_name):
|
||||
async def attribute_method(p):
|
||||
return getattr(p, attribute_name)
|
||||
return attribute_method
|
||||
|
||||
@classmethod
|
||||
def data_attribute_by_name(cls, attribute_name):
|
||||
async def attribute_method(p):
|
||||
return getattr(p.data, attribute_name)
|
||||
return attribute_method
|
||||
|
||||
@classmethod
|
||||
def setup_default_builder(cls, string_builder):
|
||||
string_builder.update({
|
||||
'ID': PenguinStringCompiler.data_attribute_by_name('ID'),
|
||||
'Nickname': PenguinStringCompiler.attribute_by_name('Nickname'),
|
||||
'Approval': PenguinStringCompiler.attribute_by_name('approval'),
|
||||
'Color': PenguinStringCompiler.data_attribute_by_name('Color'),
|
||||
'Head': PenguinStringCompiler.data_attribute_by_name('Head'),
|
||||
'Face': PenguinStringCompiler.data_attribute_by_name('Face'),
|
||||
'Neck': PenguinStringCompiler.data_attribute_by_name('Neck'),
|
||||
'Body': PenguinStringCompiler.data_attribute_by_name('Body'),
|
||||
'Hand': PenguinStringCompiler.data_attribute_by_name('Name'),
|
||||
'Photo': PenguinStringCompiler.data_attribute_by_name('Photo'),
|
||||
'X': PenguinStringCompiler.attribute_by_name('x'),
|
||||
'Y': PenguinStringCompiler.attribute_by_name('y'),
|
||||
'Frame': PenguinStringCompiler.attribute_by_name('frame'),
|
||||
'Member': PenguinStringCompiler.attribute_by_name('member'),
|
||||
'MemberDays': PenguinStringCompiler.attribute_by_name('membership_days'),
|
||||
'Avatar': PenguinStringCompiler.attribute_by_name('avatar'),
|
||||
'PenguinState': PenguinStringCompiler.attribute_by_name('penguin_state'),
|
||||
'PartyState': PenguinStringCompiler.attribute_by_name('party_state'),
|
||||
'PuffleState': PenguinStringCompiler.attribute_by_name('puffle_state')
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def setup_anonymous_default_builder(cls, string_builder):
|
||||
string_builder.update({
|
||||
'ID': PenguinStringCompiler.attribute_by_name('ID'),
|
||||
'Nickname': PenguinStringCompiler.attribute_by_name('Nickname'),
|
||||
'Approval': PenguinStringCompiler.attribute_by_name('approval'),
|
||||
'Color': PenguinStringCompiler.attribute_by_name('Color'),
|
||||
'Head': PenguinStringCompiler.attribute_by_name('Head'),
|
||||
'Face': PenguinStringCompiler.attribute_by_name('Face'),
|
||||
'Neck': PenguinStringCompiler.attribute_by_name('Neck'),
|
||||
'Body': PenguinStringCompiler.attribute_by_name('Body'),
|
||||
'Hand': PenguinStringCompiler.attribute_by_name('Name'),
|
||||
'Photo': PenguinStringCompiler.attribute_by_name('Photo')
|
||||
})
|
||||
Reference in New Issue
Block a user