Rename all modules to comply with PEP8

Lowercase with underscores
This commit is contained in:
Ben
2019-05-16 16:34:16 +01:00
parent 5a77c09ac7
commit 75c05604d9
59 changed files with 116 additions and 100 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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"))

View File

@@ -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')

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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!')

View File

@@ -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!')

View File

@@ -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

View File

@@ -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

View File

@@ -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'})

View File

@@ -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)

View File

@@ -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()

View File

@@ -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__()

View File

@@ -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

View File

@@ -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)

View File

@@ -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')
})