async def EquipItem(self, instance_id: int32): item_inst = self.Items.get(instance_id, None) if item_inst is None: return \ WARN_MSG(f"There is no item instance with id={instance_id}") item_info = ItemsCatalog.get_by("Name", item_inst['Name']) if item_info is None: return \ WARN_MSG(f"There is no item named {item_inst['Name']}") slot_id = item_info['CompatibleSlot'] with Sync(self.Equipment): self.Equipment[slot_id] = instance_id
def terminate(self): INFO_MSG(f"Service {self.get_class_name()} going to be stopped") def on_terminate(proc): process_info = self.processes.get(proc.pid) status = proc.status() WARN_MSG( "Process did't terminated after timeout. pid: %s, status: %s, info: %s", proc.pid, status, process_info) processes = [] for pid, info in self.processes.items(): process = psutil.Process(pid) processes.append(process) process.terminate() INFO_MSG( f"Service {self.get_class_name()}. Send terminate to {info['name']}, status: {process.status()}, pid_exists: {psutil.pid_exists(pid)}" ) if processes: try: from time import sleep # sleep(5) # for i in processes: # INFO_MSG(f"1 Service {self.get_class_name()}. -- {i.pid} --, status: {i.status()}, pid_exists: {psutil.pid_exists(i.pid)}") gone, alive = psutil.wait_procs([ i for i in processes if i.status() != psutil.STATUS_ZOMBIE ], timeout=60, callback=on_terminate) # todo: Some strange sings here except Exception as e: WARN_MSG(f"Service {self.get_class_name()} exception {e}") INFO_MSG(f"Service {self.get_class_name()} is stopped") sys.exit(0)
def __run__(cls, *args, **kwargs): Globals.access_token = Access().register_access( "SUPER INTERNAL ACCESS", AccessLevel.Internal) Globals.service_name = cls.__name__ + CommandLine.get_arguments( ).postfix service_instance = cls(*args, **kwargs) Globals.this_service = service_instance signal.signal(signal.SIGTERM, service_instance.sigterm_handler) # signal.signal(signal.SIGBREAK, service_instance.sigterm_handler) signal.signal(signal.SIGINT, service_instance.sigterm_handler) try: asyncio.get_event_loop().run_until_complete( service_instance) # this calls __await__ asyncio.get_event_loop().run_until_complete( service_instance.done()) asyncio.get_event_loop().run_forever() except KeyboardInterrupt: INFO_MSG("Preparing to shut down service...") except Exception as exc: from traceback import print_exc ERROR_MSG("Exception raised:", exc) print_exc() INFO_MSG("Press enter to continue") input() print("What's your name?\n> ", end="") name = input() print(f"{name}, how did you come to this?\n> ", end="") reason = input() WARN_MSG( f'The program has been stopped, the {name} provoked an error, and says: "{reason}". Dismiss him' )
def on_service_connection_lost(self, process: subprocess.Popen, connection): try: process.wait(1.0) except subprocess.TimeoutExpired: WARN_MSG( f"Something went wrong with process {process.pid}. Timeout expired" ) return if process.returncode: WARN_MSG( f"Process {process.pid} has been disconnected with return code {process.returncode} ({get_retcode_descr(process.returncode)}) " ) else: WARN_MSG(f"Process {process.pid} just disconnected")
async def enqueue_player(self, session): """ Добавить в очередь игрока Запускает матч, если список игроков заполнен @param session: сессия игрока @param character_id: ИД персонажа """ INFO_MSG(f"Enqueueing player {session}") request_result = EReqGameResponse.Wait if self.fulfilled: WARN_MSG(f"Can't enqueue {session}. Game is fulfilled") return EReqGameResponse.Denied self.players[session] = GamePlayerInfo(session=session) session.ue4client.add_lost_callback( partial(self.on_client_dropped, session)) # Начать выполнение матча. Можно только если команда заполнена и матч на данный момент не выполняется if self.fulfilled and not self.performing: asyncio.Task(self.perform()) # Допустить игрока (дополнительного игрока, для восполнения) в матч (можно только если уже начат) if self.started: await self.accept_player_to_started_game(session, self.players[session]) request_result = EReqGameResponse.Success return request_result
def send_method_call(self, method_index, future_id, *args, **kwargs): if not self: WARN_MSG("Call to invalid mailbox %s" % self, depth=1) return method_info = self.entity_info.get_method(self.context_name, method_index) params, _ = method_info.signature name = method_info.name serialized_params = BinarySerialization() for param_index, (param_name, param_type) in enumerate(params): param = param_type.instantiate(args[param_index]) serialized_params << param.serialize() serialized_call = BinarySerialization() serialized_call << self.remote_id serialized_call << Globals.generator_signature serialized_call << method_index serialized_call << future_id serialized_call << Globals.access_token serialized_call << serialized_params message = BinarySerialization() message << ConnectionMessageTypes.rmi_call message << serialized_call self.send_data(message.get_archive())
async def FindUserForLogin(self, username: FString, password: FString) -> (ELoginResult, int32, uint8, int32, FDateTime): """ Поиск пользователя для логина @param username: имя @param password: хэш пароля @return: успех, ИД, доступ, ДБИД """ res = await self.driver.exec_raw(""" SELECT * FROM public.users WHERE LOWER(unique_name)=LOWER(%s); """, username) for row in res: id, _, __, mail, pass_hash, salt, access, dbid, reg_time, confirmed = get_rows(row, 10) # final_hash = bcrypt.hashpw(password.encode(), salt.encode()).decode() # if pass_hash == final_hash: # if not confirmed: # WARN_MSG(f"User {username} is unconfirmed") # return ELoginResult.Unconfirmed, 0, 0, 0, FDateTime(0, 0, 0) # # res.close() return ELoginResult.Success, id, access, dbid, reg_time WARN_MSG(f"User {username} not found or wrong password") return ELoginResult.NotExists, 0, 0, 0, FDateTime.invalid()
async def async_method_caller(self, method_index, *args, **kwargs): if not self: return \ WARN_MSG("Awaiting call to invalid mailbox %s" % self, depth=1) future_data = FuturesManager().new(self, method_index) self.send_method_call(method_index, future_data['future_id'], *args) return await future_data['future']
def rollback(self, reason): WARN_MSG("rolling back, reason", reason) for property_name, ( context, serialized_value) in self.serialized_variables.items(): T = context.properties[property_name].prop_type ds = T.deserialize(serialized_value) setattr(context, property_name, ds) getattr(context, property_name).replicate()
def return_back_player(self, session): if session in self.players: asyncio.Task( self.accept_player_to_started_game(session, self.players[session], player_returned=True)) return True WARN_MSG("Failed to return back player") return False
def has_future(self, future_id, remote_entity_id, method_index): future_dict = self.futures.get(future_id, None) if future_dict is not None: if future_dict['method_id'] == method_index and future_dict[ 'mailbox'].remote_id == remote_entity_id: return True WARN_MSG( "Unable to get future with id %i, entity id %i and method index %i" % (future_id, remote_entity_id, method_index)) return False
def ensure_locked(*vars): """ Checks the all variables are locked (by transaction) and returns true. False otherwise and shows warning about unlocked variables """ result = all([var.locked for var in vars]) if not result: WARN_MSG("This variables not locked: [%s]" % ", ".join(var.property_name for var in vars if not var.locked), depth=1) return result
def reduce_classes_list(lst, context_name): from Core.Entity import Entity for i, item in enumerate(lst): if issubclass(item, Entity): lst[i] = make_mailbox_proxy(context_name, item.__name__) if item in pythonic_types_mapping: WARN_MSG("Used python type '%s' instead '%s' (replaced)" % (item.__name__, pythonic_types_mapping[item].__name__), depth=2, warning_id="TYPE_NEGLECT") lst[i] = pythonic_types_mapping[item]
async def __aexit__(self, exc_type, exc_val, exc_tb): do_not_raise_exc = True if exc_type is None: new_variables = TArray[FPropertyInfo]() for context, varname in self.vars: var = getattr(context, varname) info = FPropertyInfo(EntityDBID=context.dbid, EntityClass=context.__class__.__name__, PropertyName=var.property_name, PropertyTypeName=var.get_type_name(), SerializedValue=var.serialize()) new_variables.append(info) success = await Globals.this_service.db.UpdateVariablesTransactionally( self.old_variables, new_variables) if success: self.unlock_all() INFO_MSG("%s done" % self, depth=1) Replication.__exit__(self, exc_type, exc_val, exc_tb) else: self.rollback("DB error") self.unlock_all() ERROR_MSG( "%s failed, because values in database not actual. Variables rolled back" % self, depth=1) raise TransactionError("Variables changed outside transaction") elif exc_type == TransactionExitException: self.rollback("Interrupted") self.unlock_all() do_not_raise_exc = True WARN_MSG( "Transaction %s interrupted from code. Variables rolled back" % self, depth=1) else: self.rollback("Code error") self.unlock_all() do_not_raise_exc = False ERROR_MSG( "%s failed, because errors in code. Variables rolled back" % self, depth=1) # for context, varname in self.vars: # var = getattr(context, varname) # if var.locked: # var.unlock() return do_not_raise_exc
async def Synchronize(self): """ Синхронизировать ( типы ) """ for T in TypeBase.all_types['types'].values(): if issubclass(T, StructBase) and T is not StructBase: d = dict() for column_name, column_type in T.fields: d[column_name] = column_type.__name__ await self.driver.exec_raw(""" INSERT INTO public.types (type_name, type_data) VALUES ('{0}', '{1}') ON CONFLICT (type_name) DO UPDATE SET type_data = '{1}'::jsonb;""".format(T.__name__, json.dumps(d))) self.types = await self.get_types() classes = dict() result = await self.driver.exec_raw(""" SELECT * FROM classes; """) for r in result: class_name, data = r[0], r[1] classes[class_name] = data reconstructed_classes_info = dict() result = await self.driver.exec_raw(""" SELECT * FROM entities; """) for r in result: dbid, class_name = r[0], r[1] res = await self.driver.exec_raw(""" SELECT * FROM "class_{class_name}" WHERE db_id = {dbid} """.format(class_name=class_name, dbid=dbid)) class_data = classes[class_name] dictionary = dict() for r in res: for field_name, field_type in class_data.items(): for key, value in r.items(): T = TypeBase.find_type(field_type) if T is not None: if field_name == key: dictionary[field_name] = reconstruct(T, value) reconstructed_classes_info[(dbid, class_name)] = dictionary for (dbid, class_name), class_info in reconstructed_classes_info.items(): lst = list() for key, value in class_info.items(): lst.append('"%s" = %s' % (key, pg_str(value))) update_str = ", ".join(lst) if lst: await self.driver.exec_raw(""" UPDATE "class_{class_name}" SET {update_str} WHERE db_id = {db_id}; """.format(class_name=class_name, db_id=dbid, update_str=update_str)) else: WARN_MSG(f"There are no '{class_name}' entry with dbid {dbid}") if ConfigGlobals.ClearMissingEntitiesDBIDs: await self.driver.exec_raw(""" DELETE FROM entities WHERE db_id = {db_id}; """.format(db_id=dbid))
def destroy_game(self, game_to_destroy): INFO_MSG(f"{game_to_destroy}") for games in self.games.values(): for game in games: if game.is_subgame(game_to_destroy): game.reset() for player_session in game_to_destroy.players: self.free_player(player_session) return if game_to_destroy in games: games.remove(game_to_destroy) for player_session in game_to_destroy.players: self.free_player(player_session) return WARN_MSG(f"Cannot destroy game {game_to_destroy}, not found")
def get_citizens_with_ability_names_by_ids_and_ability_type( city, citizens_ids, ability_type_name, building_name) -> Dict[str, List[FCitizen]]: result = dict() for cid in citizens_ids: if cid not in city.Citizens: WARN_MSG(f"Failed to get citizen with id {cid}") continue citizen = city.Citizens[cid] for ability_name, ability_experience in citizen.Abilities.items(): ability_info = CitizenAbilities.get_by("Name", ability_name) if ability_info[ 'AbilityType'] == ability_type_name and building_name in ability_info[ 'BuildingTypes']: if ability_name not in result: result[ability_name] = list() result[ability_name].append(citizen) return result
async def FindUserForLoginWithoutPassword(self, username: FString) -> (ELoginResult, int32, uint8, int32, FDateTime): """ Поиск пользователя для логина @param username: имя @param password: хэш пароля @return: успех, ИД, доступ, ДБИД """ res = await self.driver.exec_raw(""" SELECT id, auth, account_dbid, register_date, confirmed FROM public.users WHERE LOWER(unique_name)=LOWER(%s); """, username) for row in res: id, access, dbid, reg_time, confirmed = get_rows(row, 5) return ELoginResult.Success, id, access, dbid, reg_time WARN_MSG(f"User {username} not found or wrong password") return ELoginResult.NotExists, 0, 0, 0, FDateTime.invalid()
async def ConfirmUser(self, digest: FString) -> (Bool, FString): unconfirmeds = await self.driver.exec_raw(""" SELECT * FROM public.users_uncofirmed WHERE digest=%s; """, digest) for unconfirmed_user in unconfirmeds: id, digest = get_rows(unconfirmed_user, 2) users = await self.driver.exec_raw(""" SELECT unique_name FROM public.users WHERE id=%s; """, id) for user in users: await self.driver.exec_raw(""" UPDATE public.users SET confirmed = TRUE WHERE id=%s; DELETE FROM users_uncofirmed WHERE id=%s; """, id, id) username = user[0] INFO_MSG(f"User {username} confirmed") return True, username else: WARN_MSG(f"No user with id={id} and digest={digest}") return False, ""
def cmd(self, cmd): if self.dedicated_server: self.dedicated_server.RunConsoleCommand(cmd) else: WARN_MSG("Can't execute command while server loading")
async def CreateClassTable(self, class_name: FString, fields: TMap[FString, FString]): """ Создать таблицу класса @param class_name: имя класса @param fields: поля {имя: тип} """ INFO_MSG(f"Create class table {class_name}, {fields}") # await self.driver.exec_raw(""" DROP TABLE IF EXISTS public.Class_{0}; """.format(class_name)) fields_substitute = str() for field_name, field_typedata in fields.items(): T = self.find_type(field_typedata) pg_spec = T.pg_spec if T else 'INTEGER' default = ConfigurationGenerator().generated_entities_info.get_by_name(class_name).get_property('base', field_name).default fields_substitute += ', "%s" %s DEFAULT %s' % (field_name, pg_spec, pg_str(default)) self.username = None current_data = await self.driver.exec_raw(""" SELECT class_data FROM public.classes WHERE class_name='{0}'; """.format(class_name)) for c in current_data: r = c['class_data'] deleted_columns = list() new_columns = list() changed_columns = list() alter_strings = list() for column_name, column_type in fields.items(): T = self.find_type(column_type) pg_spec = T.pg_spec if T else 'INTEGER' default = ConfigurationGenerator().generated_entities_info.get_by_name(class_name).get_property('base', column_name).default default = pg_str(default) if column_name not in r: new_columns.append((column_name, column_type)) alter_strings.append("ADD COLUMN {0} {1} DEFAULT {2}".format(column_name, pg_spec, default)) elif column_name in r and r[column_name] != column_type: changed_columns.append((column_name, r[column_name], column_type)) alter_strings.append("ALTER COLUMN {0} TYPE {1}, ALTER COLUMN {0} SET DEFAULT {2}".format(column_name, pg_spec, default)) for column_name, column_type in r.items(): if column_name not in fields: alter_strings.append("DROP COLUMN {0}".format(column_name)) deleted_columns.append(column_name) if deleted_columns or changed_columns or new_columns: try: await self.driver.exec_raw(""" ALTER TABLE IF EXISTS "class_{0}" {1}; """.format(class_name, ", ".join(alter_strings))) except Exception as e: ERROR_MSG("An exception occurred, returning...", e) return if deleted_columns: WARN_MSG("Deleted columns in %s %i: [%s]" % (class_name, len(deleted_columns), ", ".join(deleted_columns))) if changed_columns: WARN_MSG("Changed columns in %s %i: [%s]" % (class_name, len(changed_columns), ", ".join(["%s from %s to %s" % c for c in changed_columns]))) if new_columns: INFO_MSG("New columns in %s %i: [%s]" % (class_name, len(new_columns), ", ".join("%s %s" % c for c in new_columns))) INFO_MSG(class_name, fields, fields_substitute) await self.driver.exec_raw(""" INSERT INTO public.classes (class_name, class_data) VALUES ('{0}', '{1}') ON CONFLICT (class_name) DO UPDATE SET class_data = '{1}'; """.format(class_name, json.dumps(fields))) await self.driver.exec_raw(""" CREATE TABLE IF NOT EXISTS "class_{0}" ( rec_id SERIAL PRIMARY KEY NOT NULL, db_id SERIAL {1} ); CREATE UNIQUE INDEX IF NOT EXISTS "class_{0}_rec_id_uindex" ON "class_{0}" (rec_id); """.format(class_name, fields_substitute)) INFO_MSG(fields)
async def refresh_database(self): """ Обновляет основную структуру если она нарушена """ try: await self.driver.exec_raw(""" CREATE TABLE IF NOT EXISTS public.users --- список пользователей ( id SERIAL PRIMARY KEY NOT NULL, -- ID пользователя unique_name VARCHAR(255) NOT NULL, -- имя пользователя nickname VARCHAR(255), mail VARCHAR(255) NOT NULL, hash VARCHAR(255), -- пароль salt VARCHAR(255), auth INT2, -- аутентификация account_dbid SERIAL, -- DB-идентификатор сущности-аккаунта register_date TIMESTAMP, confirmed BOOLEAN DEFAULT FALSE ); CREATE UNIQUE INDEX IF NOT EXISTS users_id_uindex ON public.users (id); CREATE INDEX IF NOT EXISTS idx_users_name ON public.users (unique_name); CREATE TABLE IF NOT EXISTS public.users_uncofirmed ( id SERIAL PRIMARY KEY NOT NULL, digest VARCHAR(255) ); CREATE TABLE IF NOT EXISTS public.entities --- список сущностей (DB-идентификатор - имя класса) ( db_id SERIAL PRIMARY KEY NOT NULL, -- DB-идентификатор сущности class_name VARCHAR(255) -- имя класс ); CREATE UNIQUE INDEX IF NOT EXISTS entities_db_id_uindex ON public.entities (db_id); CREATE TABLE IF NOT EXISTS public.classes --- имформация о классах в базе данных ( class_name VARCHAR(255) PRIMARY KEY NOT NULL, -- имя класса class_data JSONB -- данные класса ); CREATE UNIQUE INDEX IF NOT EXISTS classes_class_name_uindex ON public.classes (class_name); CREATE TABLE IF NOT EXISTS public.types --- имформация о типах ( type_name VARCHAR(255) PRIMARY KEY NOT NULL, -- имя типа type_data JSONB -- данные типа ); CREATE UNIQUE INDEX IF NOT EXISTS types_type_name_uindex ON public.types (type_name); CREATE TABLE IF NOT EXISTS ccu ( app_name VARCHAR(255), dump_time TIMESTAMP, online_count INTEGER, in_game_count INTEGER ); --- CREATE TABLE IF NOT EXISTS public.storages --- имформация о хранилищах в базе данных --- ( --- storage_name VARCHAR(255) PRIMARY KEY NOT NULL, -- имя хранилища --- storage_data JSONB -- данные хранилища --- ); --- CREATE UNIQUE INDEX IF NOT EXISTS storages_class_name_uindex ON public.storages (storage_name); CREATE TABLE IF NOT EXISTS public.statistics ( id SERIAL PRIMARY KEY NOT NULL, user_id INTEGER, -- ID пользователя username VARCHAR(255), score INTEGER DEFAULT 0, game_mode INTEGER, deaths INTEGER DEFAULT 0, wins INTEGER DEFAULT 0, CONSTRAINT unique_entry UNIQUE (username, game_mode) ); """) except psycopg2.ProgrammingError as e: WARN_MSG(f"Unable to refresh database due to errors: {e}")
def __init__(self, name): from Core import WARN_MSG self.name = name WARN_MSG(f"Access to invalid configuration '{self.name}'")
def on_terminate(proc): process_info = self.processes.get(proc.pid) status = proc.status() WARN_MSG( "Process did't terminated after timeout. pid: %s, status: %s, info: %s", proc.pid, status, process_info)
async def handle_message(self, data): """ Обработка входящего сообщения """ try: message_proxy = BinarySerialization(data).proxy() except Exception as e: ERROR_MSG("Unable to make proxy for data %s: %s" % (data, e)) return try: message_type = message_proxy >> int message_data = message_proxy >> bytes except: print(message_proxy) if message_type == ConnectionMessageTypes.rmi_call: proxy = BinarySerialization(message_data).proxy() entity_id = proxy >> int gen_sig = proxy >> str method_index = proxy >> int future_id = proxy >> int access_token = proxy >> str params = proxy >> bytes if gen_sig == Globals.generator_signature: await EntitiesDispatcher().execute_rmi(self.client_connection, entity_id, method_index, future_id, access_token, params) else: EntitiesDispatcher().remote_response_error(self.client_connection, entity_id, future_id, "Generator signature mismatch") elif message_type == ConnectionMessageTypes.rmi_future: proxy = BinarySerialization(message_data).proxy() entity_id = proxy >> int method_index = proxy >> int future_id = proxy >> int returns = proxy >> bytes if future_id != -1: EntitiesDispatcher().yield_rmi_result(future_id, entity_id, method_index, returns) elif message_type == ConnectionMessageTypes.rmi_error: proxy = BinarySerialization(message_data).proxy() error_source = proxy >> str error_message = proxy >> str future_id = proxy >> int WARN_MSG("Error from %s: %s" % (error_source, error_message)) if future_id != -1: EntitiesDispatcher().yield_rmi_error(future_id) elif message_type == ConnectionMessageTypes.rmi_exception: proxy = BinarySerialization(message_data).proxy() exception_source = proxy >> str exception_class = proxy >> str exception_args = proxy >> str future_id = proxy >> int WARN_MSG("Exception from %s: %s" % (exception_source, exception_class)) if future_id != -1: EntitiesDispatcher().yield_rmi_exception(future_id, exception_class, exception_args) await asyncio.sleep(0.1)
async def GenericLogin(self, login_type, unique_id, password=None, digest=None, nickname=None): INFO_MSG(f"Logging in the user {unique_id}: {password} (via {login_type})") if digest != LOGIN_DIGEST: return None, ELoginResult.InternalError, "" if password is not None: login_result_status, id, access, dbid, reg_date = await self.db.FindUserForLogin(unique_id, password) else: login_result_status, id, access, dbid, reg_date = await self.db.FindUserForLoginWithoutPassword(unique_id) # TODO: Temporary if login_result_status == ELoginResult.NotExists: WARN_MSG("Login failed, register it and try again") if password is not None: await self.RegisterUser(unique_id, "", password, False) else: if nickname is None: raise NotImplementedError("Login without password is not supported without nickname") await self.RegisterUserWithoutPassword(unique_id, nickname) return await self.GenericLogin(login_type, unique_id, password=password, digest=digest, nickname=nickname) if login_result_status != ELoginResult.Success: return None, login_result_status, "" success = False access_token = "" if id not in self.loggedin_users: region = self.config.get_region() base: 'BaseApp' = await self.supervisor.RequestBaseAppForLogin(region) if base: access_token = Access().generate(access) if nickname is None: # todo: temp nickname = unique_id success = await base.PrepareForJoin(nickname, access_token, id, dbid, self) if success: self.loggedin_users[id] = { 'base': base, 'access_token': access_token, } else: WARN_MSG(f"BaseApp does not want to take user {unique_id}") login_result_status = ELoginResult.Rejected else: login_result_status = ELoginResult.InternalError WARN_MSG(f"Failed to login user, there are no active BaseApps") else: base = self.loggedin_users[id]['base'] access_token = self.loggedin_users[id]['access_token'] success = True if not success: WARN_MSG(f"Failed to login user {unique_id} ({login_result_status})") base = None access_token = "" return base.as_exposed(), login_result_status, access_token