mirror of
https://github.com/solero/houdini.git
synced 2024-11-22 05:27:23 +00:00
Move configuration to command line only
This commit is contained in:
parent
3e7943c6db
commit
ade40f914b
144
bootstrap.py
144
bootstrap.py
@ -1,32 +1,36 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import config
|
|
||||||
|
|
||||||
from houdini.houdini import Houdini
|
from houdini.houdini import Houdini
|
||||||
from houdini.constants import ConflictResolution, Language, ClientType
|
from houdini.constants import Language, ConflictResolution, ClientType
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logger = logging.getLogger('houdini')
|
logger = logging.getLogger('houdini')
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Boot a Houdini server')
|
parser = argparse.ArgumentParser(description='Boot a Houdini server')
|
||||||
parser.add_argument('server', action='store', default='Login',
|
parser.add_argument('type', action='store', default='login',
|
||||||
help='Name of the server to boot')
|
choices=['login', 'world'], help='Name of the server to boot')
|
||||||
|
|
||||||
parser.add_argument('-id', action='store', help='Houdini server ID')
|
parser.add_argument('-id', action='store', default=3100, type=int, help='Houdini server ID')
|
||||||
parser.add_argument('-a', '--address', action='store', help='Houdini server address')
|
parser.add_argument('-n', '--name', action='store', help='Houdini server name')
|
||||||
parser.add_argument('-p', '--port', action='store', help='Houdini server port', type=int)
|
parser.add_argument('-a', '--address', action='store', default='0.0.0.0',
|
||||||
parser.add_argument('-c', '--capacity', action='store', help='Houdini server capacity', type=int)
|
help='Houdini server address')
|
||||||
parser.add_argument('-C', '--cache-expiry', dest='cache_expiry', action='store',
|
parser.add_argument('-p', '--port', action='store', help='Houdini server port', default=None, type=int)
|
||||||
|
parser.add_argument('-c', '--capacity', action='store', default=200,
|
||||||
|
help='Houdini server capacity', type=int)
|
||||||
|
parser.add_argument('-C', '--cache-expiry', dest='cache_expiry', action='store', default=3600,
|
||||||
help='Cache expiry (seconds)', type=int)
|
help='Cache expiry (seconds)', type=int)
|
||||||
parser.add_argument('-P', '--plugins', action='store',
|
parser.add_argument('-P', '--plugins', action='store', default='*',
|
||||||
nargs='*', help='Plugins to load')
|
nargs='*', help='Plugins to load')
|
||||||
parser.add_argument('-l', '--language', action='store', help='Houdini language',
|
parser.add_argument('-l', '--lang', action='store', default='en', help='Houdini language',
|
||||||
choices=['En', 'Fr', 'Pt', 'Es', 'De', 'Ru'])
|
choices=['en', 'fr', 'pt', 'es', 'de', 'ru'])
|
||||||
|
|
||||||
boot_modes = parser.add_mutually_exclusive_group()
|
login_group = parser.add_argument_group('login')
|
||||||
boot_modes.add_argument('-W', '--world', action='store_true', help='Run server in world mode')
|
login_group.add_argument('--login-failure-limit', action='store', default=5, help='Limit before flood limit',
|
||||||
boot_modes.add_argument('-L', '--login', action='store_true', help='Run server in login mode')
|
type=int)
|
||||||
|
login_group.add_argument('--login-failure-timer', action='store', default=3600, help='Timeout after flood limit',
|
||||||
|
type=int)
|
||||||
|
|
||||||
logging_group = parser.add_argument_group('logging')
|
logging_group = parser.add_argument_group('logging')
|
||||||
logging_group.add_argument('-lg', '--logging-general', action='store',
|
logging_group.add_argument('-lg', '--logging-general', action='store',
|
||||||
@ -36,139 +40,95 @@ if __name__ == '__main__':
|
|||||||
dest='logging_error_path',
|
dest='logging_error_path',
|
||||||
help='Error log path')
|
help='Error log path')
|
||||||
logging_group.add_argument('-ll', '--logging-level', action='store',
|
logging_group.add_argument('-ll', '--logging-level', action='store',
|
||||||
|
default='INFO',
|
||||||
dest='logging_level',
|
dest='logging_level',
|
||||||
help='Logging level')
|
help='Logging level')
|
||||||
|
|
||||||
database_group = parser.add_argument_group('database')
|
database_group = parser.add_argument_group('database')
|
||||||
database_group.add_argument('-da', '--database-address', action='store',
|
database_group.add_argument('-da', '--database-address', action='store',
|
||||||
dest='database_address',
|
dest='database_address',
|
||||||
default=config.database['Address'],
|
default='localhost',
|
||||||
help='Postgresql database address')
|
help='Postgresql database address')
|
||||||
database_group.add_argument('-du', '--database-username', action='store',
|
database_group.add_argument('-du', '--database-username', action='store',
|
||||||
dest='database_username',
|
dest='database_username',
|
||||||
default=config.database['Username'],
|
default='postgres',
|
||||||
help='Postgresql database username')
|
help='Postgresql database username')
|
||||||
database_group.add_argument('-dp', '--database-password', action='store',
|
database_group.add_argument('-dp', '--database-password', action='store',
|
||||||
dest='database_password',
|
dest='database_password',
|
||||||
default=config.database['Password'],
|
default='password',
|
||||||
help='Postgresql database password')
|
help='Postgresql database password')
|
||||||
database_group.add_argument('-dn', '--database-name', action='store',
|
database_group.add_argument('-dn', '--database-name', action='store',
|
||||||
dest='database_name',
|
dest='database_name',
|
||||||
default=config.database['Name'],
|
default='postgres',
|
||||||
help='Postgresql database name')
|
help='Postgresql database name')
|
||||||
|
|
||||||
redis_group = parser.add_argument_group('redis')
|
redis_group = parser.add_argument_group('redis')
|
||||||
redis_group.add_argument('-ra', '--redis-address', action='store',
|
redis_group.add_argument('-ra', '--redis-address', action='store',
|
||||||
dest='redis_address',
|
dest='redis_address',
|
||||||
default=config.redis['Address'],
|
default='localhost',
|
||||||
help='Redis server address')
|
help='Redis server address')
|
||||||
redis_group.add_argument('-rp', '--redis-port', action='store',
|
redis_group.add_argument('-rp', '--redis-port', action='store',
|
||||||
dest='redis_port',
|
dest='redis_port',
|
||||||
type=int,
|
type=int,
|
||||||
default=config.redis['Port'],
|
default=6379,
|
||||||
help='Redis server port')
|
help='Redis server port')
|
||||||
|
|
||||||
command_group = parser.add_argument_group('commands')
|
command_group = parser.add_argument_group('commands')
|
||||||
command_group.add_argument('-cp', '--command-prefix', action='store', dest='command_prefix',
|
command_group.add_argument('-cp', '--command-prefix', action='store', dest='command_prefix',
|
||||||
nargs='*',
|
nargs='*',
|
||||||
default=config.commands['Prefix'],
|
default=['!', '?', '.'],
|
||||||
help='Command prefixes')
|
help='Command prefixes')
|
||||||
command_group.add_argument('-csd', '--command-string-delimiters', action='store', dest='command_string_delimiters',
|
command_group.add_argument('-csd', '--command-string-delimiters', action='store', dest='command_string_delimiters',
|
||||||
nargs='*',
|
nargs='*',
|
||||||
default=config.commands['StringDelimiters'],
|
default=['"', "'"],
|
||||||
help='Command string delimiters')
|
help='Command string delimiters')
|
||||||
command_group.add_argument('-ccm', '--command-conflict-mode', action='store', dest='command_conflict_mode',
|
command_group.add_argument('-ccm', '--command-conflict-mode', action='store', dest='command_conflict_mode',
|
||||||
default=config.commands['ConflictMode'].name,
|
default='silent',
|
||||||
help='Command conflict mode', choices=['Silent', 'Append', 'Exception'])
|
help='Command conflict mode', choices=['silent', 'append', 'exception'])
|
||||||
|
|
||||||
|
games_group = parser.add_argument_group('games')
|
||||||
|
games_group.add_argument('--max-coins', action='store',
|
||||||
|
default=1000000, type=int, help='Max coins earnable')
|
||||||
|
games_group.add_argument('--max-coins-per-min', action='store',
|
||||||
|
default=250, type=int, help='Max coins per min')
|
||||||
|
|
||||||
client_group = parser.add_argument_group('client')
|
client_group = parser.add_argument_group('client')
|
||||||
client_mode = client_group.add_mutually_exclusive_group()
|
client_mode = client_group.add_mutually_exclusive_group()
|
||||||
client_mode.add_argument('--multi-client-mode', action='store_true',
|
|
||||||
help='Run server with support for both clients')
|
|
||||||
client_mode.add_argument('--single-client-mode', action='store_true',
|
client_mode.add_argument('--single-client-mode', action='store_true',
|
||||||
help='Run server with support for default client only')
|
help='Run server with support for default client only')
|
||||||
client_group.add_argument('--legacy-version', action='store',
|
client_group.add_argument('--legacy-version', action='store',
|
||||||
type=int,
|
type=int,
|
||||||
default=config.client['LegacyVersionChk'],
|
default=153,
|
||||||
help='Legacy client version to identify legacy clients')
|
help='Legacy client version to identify legacy clients')
|
||||||
client_group.add_argument('--vanilla-version', action='store',
|
client_group.add_argument('--vanilla-version', action='store',
|
||||||
type=int,
|
type=int,
|
||||||
default=config.client['VanillaVersionChk'],
|
default=253,
|
||||||
help='Vanilla client version to identify vanilla clients')
|
help='Vanilla client version to identify vanilla clients')
|
||||||
client_group.add_argument('--default-version', action='store',
|
client_group.add_argument('--default-version', action='store',
|
||||||
type=int,
|
type=int,
|
||||||
default=config.client['DefaultVersionChk'],
|
default=153,
|
||||||
help='Default version to identify clients when multi-client is off')
|
help='Default version to identify clients when multi-client is off')
|
||||||
client_group.add_argument('--default-client', action='store',
|
client_group.add_argument('--default-client', action='store',
|
||||||
choices=['Legacy', 'Vanilla'],
|
choices=['legacy', 'vanilla'],
|
||||||
default=config.client['DefaultClientType'].name,
|
default='legacy',
|
||||||
help='Default client when multi-client is off')
|
help='Default client when multi-client is off')
|
||||||
client_group.add_argument('-k', '--auth-key', action='store',
|
client_group.add_argument('-k', '--auth-key', action='store',
|
||||||
default='houdini',
|
default='houdini',
|
||||||
help='Static key to use in place of the deprecated random key')
|
help='Static key to use in place of the deprecated random key')
|
||||||
|
client_group.add_argument('-kt', '--auth-ttl', action='store', type=int, default=3000,
|
||||||
|
help='Auth key TTL (seconds)')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
database = {
|
args.port = args.port if args.port else 9875 if args.type == 'world' else 6112
|
||||||
'Address': args.database_address,
|
args.name = args.name if args.name else 'World' if args.type == 'world' else 'Login'
|
||||||
'Username': args.database_username,
|
args.lang = dict(en=Language.En, fr=Language.Fr, pt=Language.Pt,
|
||||||
'Password': args.database_password,
|
es=Language.Es, de=Language.De, ru=Language.Ru).get(args.lang)
|
||||||
'Name': args.database_name
|
args.command_conflict_mode = dict(silent=ConflictResolution.Silent, append=ConflictResolution.Append,
|
||||||
}
|
exception=ConflictResolution.Exception).get(args.command_conflict_mode)
|
||||||
|
args.default_client = dict(legacy=ClientType.Legacy, vanilla=ClientType.Vanilla).get(args.default_client)
|
||||||
|
|
||||||
redis = {
|
factory_instance = Houdini(args)
|
||||||
'Address': args.redis_address,
|
|
||||||
'Port': args.redis_port
|
|
||||||
}
|
|
||||||
|
|
||||||
commands = {
|
|
||||||
'Prefix': args.command_prefix,
|
|
||||||
'StringDelimiters': args.command_string_delimiters,
|
|
||||||
'ConflictMode': getattr(ConflictResolution, args.command_conflict_mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
client = {
|
|
||||||
'MultiClientSupport': True if args.multi_client_mode else False if args.single_client_mode
|
|
||||||
else config.client['MultiClientSupport'],
|
|
||||||
'LegacyVersionChk': args.legacy_version,
|
|
||||||
'VanillaVersionChk': args.vanilla_version,
|
|
||||||
|
|
||||||
'DefaultVersionChk': args.default_version,
|
|
||||||
'DefaultClientType': getattr(ClientType, args.default_client),
|
|
||||||
|
|
||||||
'AuthStaticKey': args.auth_key
|
|
||||||
}
|
|
||||||
|
|
||||||
server = {
|
|
||||||
'Address': args.address or config.servers[args.server]['Address'],
|
|
||||||
'Port': args.port or config.servers[args.server]['Port'],
|
|
||||||
'World': True if args.world else False if args.login else None or config.servers[args.server]['World'],
|
|
||||||
'Plugins': True if args.plugins and '*' in args.plugins
|
|
||||||
else args.plugins or config.servers[args.server]['Plugins']
|
|
||||||
}
|
|
||||||
|
|
||||||
logging = {
|
|
||||||
'General': args.logging_general_path or config.servers[args.server]['Logging']['General'],
|
|
||||||
'Errors': args.logging_error_path or config.servers[args.server]['Logging']['Errors'],
|
|
||||||
'Level': args.logging_level or config.servers[args.server]['Logging']['Level']
|
|
||||||
}
|
|
||||||
|
|
||||||
if server['World']:
|
|
||||||
server.update({
|
|
||||||
'Id': args.id or config.servers[args.server]['Id'],
|
|
||||||
'Language': getattr(Language, args.language) if args.language else config.servers[args.server]['Language'],
|
|
||||||
'Capacity': args.capacity or config.servers[args.server]['Capacity'],
|
|
||||||
'CacheExpiry': args.cache_expiry or config.servers[args.server]['CacheExpiry']
|
|
||||||
})
|
|
||||||
|
|
||||||
server['Logging'] = logging
|
|
||||||
|
|
||||||
factory_instance = Houdini(args.server,
|
|
||||||
database=database,
|
|
||||||
redis=redis,
|
|
||||||
commands=commands,
|
|
||||||
client=client,
|
|
||||||
server=server)
|
|
||||||
try:
|
try:
|
||||||
asyncio.run(factory_instance.start())
|
asyncio.run(factory_instance.start())
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
from houdini.constants import ConflictResolution, Language, ClientType
|
|
||||||
|
|
||||||
database = {
|
|
||||||
'Address': 'localhost',
|
|
||||||
'Username': 'postgres',
|
|
||||||
'Password': 'password',
|
|
||||||
'Name': 'houdini',
|
|
||||||
}
|
|
||||||
|
|
||||||
redis = {
|
|
||||||
'Address': '127.0.0.1',
|
|
||||||
'Port': 6379
|
|
||||||
}
|
|
||||||
|
|
||||||
commands = {
|
|
||||||
'Prefix': ['!', '?', '.'],
|
|
||||||
'StringDelimiters': ['"', "'"],
|
|
||||||
'ConflictMode': ConflictResolution.Silent
|
|
||||||
}
|
|
||||||
|
|
||||||
client = {
|
|
||||||
'MultiClientSupport': True,
|
|
||||||
'LegacyVersionChk': 153,
|
|
||||||
'VanillaVersionChk': 253,
|
|
||||||
|
|
||||||
'DefaultVersionChk': 253,
|
|
||||||
'DefaultClientType': ClientType.Vanilla,
|
|
||||||
|
|
||||||
'AuthStaticKey': 'houdini'
|
|
||||||
}
|
|
||||||
|
|
||||||
servers = {
|
|
||||||
'Login': {
|
|
||||||
'Address': '0.0.0.0',
|
|
||||||
'Port': 6112,
|
|
||||||
'World': False,
|
|
||||||
'Plugins': [
|
|
||||||
'Example'
|
|
||||||
],
|
|
||||||
'Logging': {
|
|
||||||
'General': 'logs/login.log',
|
|
||||||
'Errors': 'logs/login-errors.log',
|
|
||||||
'Level': 'DEBUG'
|
|
||||||
},
|
|
||||||
'LoginFailureLimit': 5,
|
|
||||||
'LoginFailureTimer': 3600,
|
|
||||||
'KeyTTL': 3000
|
|
||||||
},
|
|
||||||
'Blizzard': {
|
|
||||||
'Id': '100',
|
|
||||||
'Address': '0.0.0.0',
|
|
||||||
'Port': 9875,
|
|
||||||
'Language': Language.En,
|
|
||||||
'World': True,
|
|
||||||
'Capacity': 200,
|
|
||||||
'CacheExpiry': 3600,
|
|
||||||
'Plugins': [
|
|
||||||
],
|
|
||||||
'Logging': {
|
|
||||||
'General': 'logs/blizzard.log',
|
|
||||||
'Errors': 'logs/blizzard-errors.log',
|
|
||||||
'Level': 'INFO'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import config
|
|
||||||
|
|
||||||
from houdini import handlers
|
from houdini import handlers
|
||||||
from houdini import plugins
|
from houdini import plugins
|
||||||
@ -46,12 +45,12 @@ class _CommandGroup(_Command):
|
|||||||
|
|
||||||
|
|
||||||
def command(name=None, **kwargs):
|
def command(name=None, **kwargs):
|
||||||
return _listener(_Command, name, string_delimiter=config.commands['StringDelimiters'],
|
return _listener(_Command, name, string_delimiter=['"', "'"],
|
||||||
string_separator=' ', **kwargs)
|
string_separator=' ', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def group(name=None, **kwargs):
|
def group(name=None, **kwargs):
|
||||||
return _listener(_CommandGroup, name, string_delimiter=config.commands['StringDelimiters'],
|
return _listener(_CommandGroup, name, string_delimiter=['"', "'"],
|
||||||
string_separator=' ', **kwargs)
|
string_separator=' ', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +58,6 @@ cooldown = handlers.cooldown
|
|||||||
check = handlers.check
|
check = handlers.check
|
||||||
|
|
||||||
player_attribute = handlers.player_attribute
|
player_attribute = handlers.player_attribute
|
||||||
player_data_attribute = handlers.player_data_attribute
|
|
||||||
player_in_room = handlers.player_in_room
|
player_in_room = handlers.player_in_room
|
||||||
|
|
||||||
|
|
||||||
@ -84,13 +82,14 @@ class CommandManager(_AbstractManager):
|
|||||||
for name in command_object.alias:
|
for name in command_object.alias:
|
||||||
if name in parent_commands and len(parent_commands[name]):
|
if name in parent_commands and len(parent_commands[name]):
|
||||||
conflict_command = parent_commands[name][0]
|
conflict_command = parent_commands[name][0]
|
||||||
if config.commands['ConflictMode'] == ConflictResolution.Exception:
|
conflict_resolution = self.server.config.command_conflict_mode
|
||||||
|
if conflict_resolution == ConflictResolution.Exception:
|
||||||
raise NameError(f'Command name conflict: \'{name}\' from plugin \'{module.__class__.__name__}\' '
|
raise NameError(f'Command name conflict: \'{name}\' from plugin \'{module.__class__.__name__}\' '
|
||||||
f'conflicts with \'{conflict_command.name}\' from '
|
f'conflicts with \'{conflict_command.name}\' from '
|
||||||
f'module \'{conflict_command.instance.__class__.__name__}\'')
|
f'module \'{conflict_command.instance.__class__.__name__}\'')
|
||||||
elif config.commands['ConflictMode'] == ConflictResolution.Append:
|
elif conflict_resolution == ConflictResolution.Append:
|
||||||
parent_commands[name].append(command_object)
|
parent_commands[name].append(command_object)
|
||||||
elif config.commands['ConflictMode'] == ConflictResolution.Silent:
|
elif conflict_resolution == ConflictResolution.Silent:
|
||||||
module.server.logger.warning(f'Command \'{name}\' from module \'{module.__class__.__name__}\' '
|
module.server.logger.warning(f'Command \'{name}\' from module \'{module.__class__.__name__}\' '
|
||||||
f'disabled due to conflict with '
|
f'disabled due to conflict with '
|
||||||
f'\'{conflict_command.instance.__class__.__name__}\'')
|
f'\'{conflict_command.instance.__class__.__name__}\'')
|
||||||
@ -102,25 +101,21 @@ def is_command(command_object):
|
|||||||
return issubclass(type(command_object), _Command)
|
return issubclass(type(command_object), _Command)
|
||||||
|
|
||||||
|
|
||||||
if type(config.commands['Prefix']) == str:
|
def has_command_prefix(pre, command_string):
|
||||||
config.commands['Prefix'] = [config.commands['Prefix']]
|
for prefix in pre:
|
||||||
|
|
||||||
|
|
||||||
def has_command_prefix(command_string):
|
|
||||||
for prefix in config.commands['Prefix']:
|
|
||||||
if command_string.startswith(prefix):
|
if command_string.startswith(prefix):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_command_prefix(command_string):
|
def get_command_prefix(pre, command_string):
|
||||||
for prefix in config.commands['Prefix']:
|
for prefix in pre:
|
||||||
if command_string.startswith(prefix):
|
if command_string.startswith(prefix):
|
||||||
return prefix
|
return prefix
|
||||||
|
|
||||||
|
|
||||||
async def invoke_command_string(commands, p, command_string):
|
async def invoke_command_string(commands, p, command_string):
|
||||||
prefix = get_command_prefix(command_string)
|
prefix = get_command_prefix(p.server.config.command_prefix, command_string)
|
||||||
no_prefix = command_string[len(prefix):]
|
no_prefix = command_string[len(prefix):]
|
||||||
data = no_prefix.split(' ')
|
data = no_prefix.split(' ')
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import config
|
|
||||||
|
|
||||||
from houdini import handlers
|
from houdini import handlers
|
||||||
from houdini.handlers import XMLPacket
|
from houdini.handlers import XMLPacket
|
||||||
from houdini.converters import VersionChkConverter
|
from houdini.converters import VersionChkConverter
|
||||||
@ -11,13 +9,13 @@ from houdini.data.buddy import BuddyList
|
|||||||
@handlers.handler(XMLPacket('verChk'))
|
@handlers.handler(XMLPacket('verChk'))
|
||||||
@handlers.allow_once
|
@handlers.allow_once
|
||||||
async def handle_version_check(p, version: VersionChkConverter):
|
async def handle_version_check(p, version: VersionChkConverter):
|
||||||
if config.client['MultiClientSupport']:
|
if not p.server.config.single_client_mode:
|
||||||
if config.client['LegacyVersionChk'] == version:
|
if p.server.config.legacy_version == version:
|
||||||
p.client_type = ClientType.Legacy
|
p.client_type = ClientType.Legacy
|
||||||
elif config.client['VanillaVersionChk'] == version:
|
elif p.server.config.vanilla_version == version:
|
||||||
p.client_type = ClientType.Vanilla
|
p.client_type = ClientType.Vanilla
|
||||||
elif config.client['DefaultVersionChk'] == version:
|
elif p.server.config.default_version == version:
|
||||||
p.client_type = config.client['DefaultClientType']
|
p.client_type = p.server.config.default_version
|
||||||
|
|
||||||
if p.client_type is None:
|
if p.client_type is None:
|
||||||
await p.send_xml({'body': {'action': 'apiKO', 'r': '0'}})
|
await p.send_xml({'body': {'action': 'apiKO', 'r': '0'}})
|
||||||
@ -28,32 +26,31 @@ async def handle_version_check(p, version: VersionChkConverter):
|
|||||||
|
|
||||||
@handlers.handler(XMLPacket('rndK'))
|
@handlers.handler(XMLPacket('rndK'))
|
||||||
@handlers.allow_once
|
@handlers.allow_once
|
||||||
async def handle_random_key(p, data):
|
async def handle_random_key(p, _):
|
||||||
await p.send_xml({'body': {'action': 'rndK', 'r': '-1'}, 'k': config.client['AuthStaticKey']})
|
await p.send_xml({'body': {'action': 'rndK', 'r': '-1'}, 'k': p.server.config.auth_key})
|
||||||
|
|
||||||
|
|
||||||
async def get_server_presence(p, pid):
|
async def get_server_presence(p, pid):
|
||||||
buddy_worlds = []
|
buddy_worlds = []
|
||||||
world_populations = []
|
world_populations = []
|
||||||
|
|
||||||
for server_name, server_config in config.servers.items():
|
pops = await p.server.redis.hgetall('houdini.population')
|
||||||
if server_config['World']:
|
for server_id, server_population in pops.items():
|
||||||
server_population = await p.server.redis.hget('houdini.population', server_config['Id'])
|
server_population = (7 if int(server_population) == p.server.config.capacity
|
||||||
server_population = (7 if int(server_population) == server_config['Capacity']
|
else int(server_population) // (p.server.config.capacity // 6)) \
|
||||||
else int(server_population) // (server_config['Capacity'] // 6)) \
|
if server_population else 0
|
||||||
if server_population else 0
|
|
||||||
|
|
||||||
world_populations.append(f'{server_config["Id"]},{server_population}')
|
world_populations.append(f'{int(server_id)},{int(server_population)}')
|
||||||
|
|
||||||
server_key = f'houdini.players.{server_config["Id"]}'
|
server_key = f'houdini.players.{int(server_id)}'
|
||||||
if await p.server.redis.scard(server_key):
|
if await p.server.redis.scard(server_key):
|
||||||
async with p.server.db.transaction():
|
async with p.server.db.transaction():
|
||||||
buddies = BuddyList.select('buddy_id').where(BuddyList.penguin_id == pid).gino.iterate()
|
buddies = BuddyList.select('buddy_id').where(BuddyList.penguin_id == pid).gino.iterate()
|
||||||
tr = p.server.redis.multi_exec()
|
tr = p.server.redis.multi_exec()
|
||||||
async for buddy_id, in buddies:
|
async for buddy_id, in buddies:
|
||||||
tr.sismember(server_key, buddy_id)
|
tr.sismember(server_key, buddy_id)
|
||||||
online_buddies = await tr.execute()
|
online_buddies = await tr.execute()
|
||||||
if any(online_buddies):
|
if any(online_buddies):
|
||||||
buddy_worlds.append(server_config['Id'])
|
buddy_worlds.append(str(int(server_id)))
|
||||||
|
|
||||||
return '|'.join(world_populations), '|'.join(buddy_worlds)
|
return '|'.join(world_populations), '|'.join(buddy_worlds)
|
||||||
|
@ -41,19 +41,19 @@ async def handle_login(p, credentials: Credentials):
|
|||||||
if await p.server.redis.exists(flood_key):
|
if await p.server.redis.exists(flood_key):
|
||||||
tr = p.server.redis.multi_exec()
|
tr = p.server.redis.multi_exec()
|
||||||
tr.incr(flood_key)
|
tr.incr(flood_key)
|
||||||
tr.expire(flood_key, p.server.server_config['LoginFailureTimer'])
|
tr.expire(flood_key, p.server.config.login_failure_timer)
|
||||||
failure_count, _ = await tr.execute()
|
failure_count, _ = await tr.execute()
|
||||||
|
|
||||||
if failure_count >= p.server.server_config['LoginFailureLimit']:
|
if failure_count >= p.server.config.login_failure_limit:
|
||||||
return await p.send_error_and_disconnect(150)
|
return await p.send_error_and_disconnect(150)
|
||||||
else:
|
else:
|
||||||
await p.server.redis.setex(flood_key, p.server.server_config['LoginFailureTimer'], 1)
|
await p.server.redis.setex(flood_key, p.server.config.login_failure_timer, 1)
|
||||||
|
|
||||||
return await p.send_error_and_disconnect(101)
|
return await p.send_error_and_disconnect(101)
|
||||||
|
|
||||||
failure_count = await p.server.redis.get(flood_key)
|
failure_count = await p.server.redis.get(flood_key)
|
||||||
if failure_count:
|
if failure_count:
|
||||||
max_attempts_exceeded = int(failure_count) >= p.server.server_config['LoginFailureLimit']
|
max_attempts_exceeded = int(failure_count) >= p.server.config.login_failure_limit
|
||||||
|
|
||||||
if max_attempts_exceeded:
|
if max_attempts_exceeded:
|
||||||
return await p.send_error_and_disconnect(150)
|
return await p.send_error_and_disconnect(150)
|
||||||
@ -98,8 +98,8 @@ async def handle_login(p, credentials: Credentials):
|
|||||||
confirmation_hash = Crypto.hash(os.urandom(24))
|
confirmation_hash = Crypto.hash(os.urandom(24))
|
||||||
|
|
||||||
tr = p.server.redis.multi_exec()
|
tr = p.server.redis.multi_exec()
|
||||||
tr.setex(f'{data.username}.lkey', p.server.server_config['KeyTTL'], login_key)
|
tr.setex(f'{data.username}.lkey', p.server.config.auth_ttl, login_key)
|
||||||
tr.setex(f'{data.username}.ckey', p.server.server_config['KeyTTL'], confirmation_hash)
|
tr.setex(f'{data.username}.ckey', p.server.config.auth_ttl, confirmation_hash)
|
||||||
await tr.execute()
|
await tr.execute()
|
||||||
|
|
||||||
world_populations, buddy_presence = await get_server_presence(p, data.id)
|
world_populations, buddy_presence = await get_server_presence(p, data.id)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import config
|
|
||||||
|
|
||||||
from houdini import handlers
|
from houdini import handlers
|
||||||
from houdini.handlers import XMLPacket, login
|
from houdini.handlers import XMLPacket, login
|
||||||
from houdini.converters import WorldCredentials, Credentials
|
from houdini.converters import WorldCredentials, Credentials
|
||||||
@ -15,7 +13,7 @@ handle_random_key = login.handle_random_key
|
|||||||
|
|
||||||
|
|
||||||
async def world_login(p, data):
|
async def world_login(p, data):
|
||||||
if len(p.server.penguins_by_id) >= p.server.server_config['Capacity']:
|
if len(p.server.penguins_by_id) >= p.server.config.capacity:
|
||||||
return await p.send_error_and_disconnect(103)
|
return await p.send_error_and_disconnect(103)
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
@ -53,7 +51,7 @@ async def handle_login(p, credentials: WorldCredentials):
|
|||||||
return await p.close()
|
return await p.close()
|
||||||
|
|
||||||
login_key = login_key.decode()
|
login_key = login_key.decode()
|
||||||
login_hash = Crypto.encrypt_password(login_key + config.client['AuthStaticKey']) + login_key
|
login_hash = Crypto.encrypt_password(login_key + p.server.config.auth_key) + login_key
|
||||||
|
|
||||||
if credentials.client_key != login_hash:
|
if credentials.client_key != login_hash:
|
||||||
return await p.close()
|
return await p.close()
|
||||||
@ -73,11 +71,11 @@ async def handle_login(p, credentials: WorldCredentials):
|
|||||||
async def handle_legacy_login(p, credentials: Credentials):
|
async def handle_legacy_login(p, credentials: Credentials):
|
||||||
tr = p.server.redis.multi_exec()
|
tr = p.server.redis.multi_exec()
|
||||||
tr.get(f'{credentials.username}.lkey')
|
tr.get(f'{credentials.username}.lkey')
|
||||||
tr.delete(f'{credentials.username}.lkey', '{credentials.username}.ckey')
|
tr.delete(f'{credentials.username}.lkey', f'{credentials.username}.ckey')
|
||||||
login_key, _ = await tr.execute()
|
login_key, _ = await tr.execute()
|
||||||
|
|
||||||
login_key = login_key.decode()
|
login_key = login_key.decode()
|
||||||
login_hash = Crypto.encrypt_password(login_key + config.client['AuthStaticKey']) + login_key
|
login_hash = Crypto.encrypt_password(login_key + p.server.config.auth_key) + login_key
|
||||||
|
|
||||||
if login_key is None or login_hash != credentials.password:
|
if login_key is None or login_hash != credentials.password:
|
||||||
return await p.close()
|
return await p.close()
|
||||||
|
@ -15,7 +15,8 @@ async def handle_send_message(p, penguin_id: int, message: str):
|
|||||||
await penguin.send_xt("mm", message, penguin_id)
|
await penguin.send_xt("mm", message, penguin_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if has_command_prefix(message):
|
tokens = message.lower()
|
||||||
|
if has_command_prefix(p.server.config.command_prefix, message):
|
||||||
await invoke_command_string(p.server.commands, p, message)
|
await invoke_command_string(p.server.commands, p, message)
|
||||||
else:
|
else:
|
||||||
await p.room.send_xt('sm', p.id, message)
|
await p.room.send_xt('sm', p.id, message)
|
||||||
|
@ -58,9 +58,9 @@ async def handle_join_server(p, penguin_id: int, login_key: str):
|
|||||||
p.login_timestamp = datetime.now()
|
p.login_timestamp = datetime.now()
|
||||||
p.joined_world = True
|
p.joined_world = True
|
||||||
|
|
||||||
server_key = f'houdini.players.{p.server.server_config["Id"]}'
|
server_key = f'houdini.players.{p.server.config.id}'
|
||||||
await p.server.redis.sadd(server_key, p.data.id)
|
await p.server.redis.sadd(server_key, p.id)
|
||||||
await p.server.redis.hincrby('houdini.population', p.server.server_config['Id'], 1)
|
await p.server.redis.hincrby('houdini.population', p.server.config.id, 1)
|
||||||
|
|
||||||
|
|
||||||
@handlers.handler(XTPacket('j', 'jr'))
|
@handlers.handler(XTPacket('j', 'jr'))
|
||||||
@ -127,6 +127,6 @@ async def handle_disconnect_room(p):
|
|||||||
del p.server.penguins_by_id[p.id]
|
del p.server.penguins_by_id[p.id]
|
||||||
del p.server.penguins_by_username[p.username]
|
del p.server.penguins_by_username[p.username]
|
||||||
|
|
||||||
server_key = f'houdini.players.{p.server.server_config["Id"]}'
|
server_key = f'houdini.players.{p.server.config.id}'
|
||||||
await p.server.redis.srem(server_key, p.data.id)
|
await p.server.redis.srem(server_key, p.id)
|
||||||
await p.server.redis.hincrby('houdini.population', p.server.server_config['Id'], -1)
|
await p.server.redis.hincrby('houdini.population', p.server.config.id, -1)
|
||||||
|
@ -6,7 +6,6 @@ import copy
|
|||||||
from houdini.spheniscidae import Spheniscidae
|
from houdini.spheniscidae import Spheniscidae
|
||||||
from houdini.penguin import Penguin
|
from houdini.penguin import Penguin
|
||||||
from houdini import PenguinStringCompiler
|
from houdini import PenguinStringCompiler
|
||||||
import config
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
@ -47,22 +46,14 @@ from houdini.handlers.play.music import SoundStudio
|
|||||||
|
|
||||||
class Houdini:
|
class Houdini:
|
||||||
|
|
||||||
def __init__(self, server_name, **kwargs):
|
def __init__(self, config):
|
||||||
self.server = None
|
self.server = None
|
||||||
self.redis = None
|
self.redis = None
|
||||||
self.config = None
|
|
||||||
self.cache = None
|
self.cache = None
|
||||||
|
self.config = config
|
||||||
self.db = db
|
self.db = db
|
||||||
self.peers_by_ip = {}
|
self.peers_by_ip = {}
|
||||||
|
|
||||||
self.server_name = server_name
|
|
||||||
self.database_config_override = kwargs.get('database')
|
|
||||||
self.redis_config_override = kwargs.get('redis')
|
|
||||||
self.commands_config_override = kwargs.get('commands')
|
|
||||||
self.client_config_override = kwargs.get('client')
|
|
||||||
self.server_config_override = kwargs.get('server')
|
|
||||||
self.server_config = None
|
|
||||||
|
|
||||||
self.logger = None
|
self.logger = None
|
||||||
|
|
||||||
self.client_class = Spheniscidae
|
self.client_class = Spheniscidae
|
||||||
@ -105,18 +96,14 @@ class Houdini:
|
|||||||
self.music = None
|
self.music = None
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
self.config = config
|
|
||||||
|
|
||||||
self.server_config = copy.deepcopy(self.config.servers[self.server_name])
|
async def start(self):
|
||||||
self.server_config.update(self.server_config_override)
|
general_log_file = self.config.logging_general_path if self.config.logging_general_path \
|
||||||
|
else f'logs/{self.config.name.lower()}.log'
|
||||||
self.config.database.update(self.database_config_override)
|
errors_log_file = self.config.logging_error_path if self.config.logging_error_path \
|
||||||
self.config.redis.update(self.redis_config_override)
|
else f'logs/{self.config.name.lower()}-errors.log'
|
||||||
self.config.commands.update(self.commands_config_override)
|
general_log_directory = os.path.dirname(general_log_file)
|
||||||
self.config.client.update(self.client_config_override)
|
errors_log_directory = os.path.dirname(errors_log_file)
|
||||||
|
|
||||||
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):
|
if not os.path.exists(general_log_directory):
|
||||||
os.mkdir(general_log_directory)
|
os.mkdir(general_log_directory)
|
||||||
@ -125,10 +112,10 @@ class Houdini:
|
|||||||
os.mkdir(errors_log_directory)
|
os.mkdir(errors_log_directory)
|
||||||
|
|
||||||
self.logger = logging.getLogger('houdini')
|
self.logger = logging.getLogger('houdini')
|
||||||
universal_handler = RotatingFileHandler(self.server_config['Logging']['General'],
|
universal_handler = RotatingFileHandler(general_log_file,
|
||||||
maxBytes=2097152, backupCount=3, encoding='utf-8')
|
maxBytes=2097152, backupCount=3, encoding='utf-8')
|
||||||
|
|
||||||
error_handler = logging.FileHandler(self.server_config['Logging']['Errors'])
|
error_handler = logging.FileHandler(errors_log_file)
|
||||||
console_handler = logging.StreamHandler(stream=sys.stdout)
|
console_handler = logging.StreamHandler(stream=sys.stdout)
|
||||||
|
|
||||||
log_formatter = logging.Formatter('%(asctime)s [%(levelname)-5.5s] %(message)s')
|
log_formatter = logging.Formatter('%(asctime)s [%(levelname)-5.5s] %(message)s')
|
||||||
@ -141,35 +128,34 @@ class Houdini:
|
|||||||
self.logger.addHandler(console_handler)
|
self.logger.addHandler(console_handler)
|
||||||
self.logger.addHandler(error_handler)
|
self.logger.addHandler(error_handler)
|
||||||
|
|
||||||
level = logging.getLevelName(self.server_config['Logging']['Level'])
|
level = logging.getLevelName(self.config.logging_level)
|
||||||
self.logger.setLevel(level)
|
self.logger.setLevel(level)
|
||||||
|
|
||||||
self.server = await asyncio.start_server(
|
self.server = await asyncio.start_server(
|
||||||
self.client_connected, self.server_config['Address'],
|
self.client_connected, self.config.address,
|
||||||
self.server_config['Port']
|
self.config.port
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.db.set_bind('postgresql://{}:{}@{}/{}'.format(
|
await self.db.set_bind('postgresql://{}:{}@{}/{}'.format(
|
||||||
self.config.database['Username'], self.config.database['Password'],
|
self.config.database_username, self.config.database_password,
|
||||||
self.config.database['Address'],
|
self.config.database_address,
|
||||||
self.config.database['Name']))
|
self.config.database_name))
|
||||||
|
|
||||||
self.logger.info('Booting Houdini')
|
self.logger.info('Booting Houdini')
|
||||||
|
|
||||||
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
||||||
self.config.redis['Address'], self.config.redis['Port']),
|
self.config.redis_address, self.config.redis_port),
|
||||||
minsize=5, maxsize=10)
|
minsize=5, maxsize=10)
|
||||||
|
|
||||||
if self.server_config['World']:
|
if self.config.type == 'world':
|
||||||
await self.redis.delete(f'houdini.players.{self.server_config["Id"]}')
|
await self.redis.delete(f'houdini.players.{self.config.id}')
|
||||||
await self.redis.hdel(f'houdini.population', self.server_config["Id"])
|
await self.redis.hset(f'houdini.population', self.config.id, 0)
|
||||||
|
|
||||||
caches.set_config({
|
caches.set_config(dict(default=dict(
|
||||||
'default': {
|
cache=SimpleMemoryCache,
|
||||||
'cache': SimpleMemoryCache,
|
namespace='houdini',
|
||||||
'namespace': 'houdini',
|
ttl=self.config.cache_expiry
|
||||||
'ttl': self.server_config['CacheExpiry']
|
)))
|
||||||
}})
|
|
||||||
self.cache = caches.get('default')
|
self.cache = caches.get('default')
|
||||||
|
|
||||||
self.client_class = Penguin
|
self.client_class = Penguin
|
||||||
@ -227,12 +213,13 @@ class Houdini:
|
|||||||
self.logger.info(f'Loaded {len(self.characters)} characters')
|
self.logger.info(f'Loaded {len(self.characters)} characters')
|
||||||
|
|
||||||
self.permissions = await PermissionCollection.get_collection()
|
self.permissions = await PermissionCollection.get_collection()
|
||||||
|
self.chat_filter_words = await ChatFilterRuleCollection.get_collection()
|
||||||
|
|
||||||
self.logger.info(f'Multi-client support is '
|
self.logger.info(f'Multi-client support is '
|
||||||
f'{"enabled" if self.config.client["MultiClientSupport"] else "disabled"}')
|
f'{"enabled" if not self.config.single_client_mode else "disabled"}')
|
||||||
self.logger.info(f'Listening on {self.server_config["Address"]}:{self.server_config["Port"]}')
|
self.logger.info(f'Listening on {self.config.address}:{self.config.port}')
|
||||||
|
|
||||||
if self.config.client['AuthStaticKey'] != 'houdini':
|
if self.config.auth_key != 'houdini':
|
||||||
self.logger.warning('The static key has been changed from the default, '
|
self.logger.warning('The static key has been changed from the default, '
|
||||||
'this may cause authentication issues!')
|
'this may cause authentication issues!')
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
|||||||
return self.server.penguin_string_compiler.compile(self)
|
return self.server.penguin_string_compiler.compile(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nickname(self):
|
def safe_name(self):
|
||||||
return self.data.safe_nickname(self.server.server_config['Language'])
|
return self.safe_nickname(self.server.config.lang)
|
||||||
|
|
||||||
async def join_room(self, room):
|
async def join_room(self, room):
|
||||||
await room.add_penguin(self)
|
await room.add_penguin(self)
|
||||||
|
@ -34,6 +34,8 @@ class Spheniscidae:
|
|||||||
|
|
||||||
self.received_packets = set()
|
self.received_packets = set()
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_vanilla_client(self):
|
def is_vanilla_client(self):
|
||||||
return self.client_type == ClientType.Vanilla
|
return self.client_type == ClientType.Vanilla
|
||||||
@ -51,7 +53,7 @@ class Spheniscidae:
|
|||||||
|
|
||||||
async def send_policy_file(self):
|
async def send_policy_file(self):
|
||||||
await self.send_line(f'<cross-domain-policy><allow-access-from domain="*" to-ports="'
|
await self.send_line(f'<cross-domain-policy><allow-access-from domain="*" to-ports="'
|
||||||
f'{self.server.server_config["Port"]}" /></cross-domain-policy>')
|
f'{self.server.config.port}" /></cross-domain-policy>')
|
||||||
await self.close()
|
await self.close()
|
||||||
|
|
||||||
async def send_xt(self, handler_id, *data):
|
async def send_xt(self, handler_id, *data):
|
||||||
|
Loading…
Reference in New Issue
Block a user