From a923dbea1fc9eff1bd0114527d839bfafeee5646 Mon Sep 17 00:00:00 2001 From: santaspeen Date: Tue, 16 Jul 2024 17:18:49 +0300 Subject: [PATCH] [~] 0.4.5 > 0.4.6 [+] Recreate config (if empty) [+] Hidden config [!] FIX rl [!] Update getting loop [-] core_player_set_id [~] Update copyrights [~] Minor --- src/core/Client.py | 65 ++++++++++----------- src/core/Client.pyi | 4 +- src/core/__init__.py | 13 +++-- src/core/core.py | 78 ++++++++++++++++---------- src/core/tcp_server.py | 6 +- src/core/udp_server.py | 30 +++++----- src/core/udp_server.pyi | 2 +- src/modules/ConfigProvider/__init__.py | 33 ++++++++--- src/modules/RateLimiter/__init__.py | 3 +- src/modules/i18n/readme.md | 2 - src/translates/cn.json | 1 - src/translates/en.json | 1 - src/translates/ru.json | 1 - 13 files changed, 136 insertions(+), 103 deletions(-) diff --git a/src/core/Client.py b/src/core/Client.py index 11e1803..77bd52a 100644 --- a/src/core/Client.py +++ b/src/core/Client.py @@ -19,7 +19,7 @@ class Client: def __init__(self, reader, writer, core): self.__reader = reader self.__writer = writer - self.__Core = core + self._core = core self.__alive = True self.__packets_queue = [] self.__tasks = [] @@ -38,7 +38,7 @@ class Client: self._identifiers = [] self._cars = [None] * 21 # Max 20 cars per player + 1 snowman self._focus_car = -1 - self._snowman = {"id": -1, "packet": ""} + self._unicycle = {"id": -1, "packet": ""} self._connect_time = 0 self._last_position = {} self._lock = Lock() @@ -155,7 +155,7 @@ class Client: if to_all: code = chr(data[0]) - for client in self.__Core.clients: + for client in self._core.clients: if not client or (client is self and not to_self): continue if not to_udp or code in ['V', 'W', 'Y', 'E']: @@ -174,8 +174,7 @@ class Client: data = b"ABG:" + zlib.compress(data, level=zlib.Z_BEST_COMPRESSION) if to_udp: - udp_sock = self._udp_sock[0] - udp_addr = self._udp_sock[1] + udp_sock, udp_addr = self._udp_sock # self.log.debug(f'[UDP] len: {len(data)}; send: {data!r}') if udp_sock and udp_addr: try: @@ -297,7 +296,7 @@ class Client: file = data[1:].decode(config.enc) self.log.info(i18n.client_mod_request.format(repr(file))) size = -1 - for mod in self.__Core.mods_list: + for mod in self._core.mods_list: if type(mod) == int: continue if mod.get('path') == file: @@ -318,9 +317,9 @@ class Client: await self.kick("Missing download socket") return if config.Options['use_queue']: - while self.__Core.lock_upload: + while self._core.lock_upload: await asyncio.sleep(.2) - self.__Core.lock_upload = True + self._core.lock_upload = True speed = config.Options["speed_limit"] if speed: speed = speed / 2 @@ -332,8 +331,8 @@ class Client: ] sl0, sl1 = await asyncio.gather(*uploads) tr = (time.monotonic() - t) or 0.0001 - if self.__Core.lock_upload: - self.__Core.lock_upload = False + if self._core.lock_upload: + self._core.lock_upload = False msg = i18n.client_mod_sent.format(round(size / MB, 3), math.ceil(size / tr / MB), int(tr)) if speed: msg += i18n.client_mod_sent_limit.format(int(speed * 2)) @@ -349,7 +348,7 @@ class Client: elif data.startswith(b"SR"): path_list = '' size_list = '' - for mod in self.__Core.mods_list: + for mod in self._core.mods_list: if type(mod) == int: continue path_list += f"{mod['path']};" @@ -391,7 +390,7 @@ class Client: car_data = data[2:] car_id = next((i for i, car in enumerate(self._cars) if car is None), len(self._cars)) cars_count = len(self._cars) - self._cars.count(None) - if self._snowman['id'] != -1: + if self._unicycle['id'] != -1: cars_count -= 1 # -1 for unicycle self.log.debug(f"car_id={car_id}, cars_count={cars_count}") car_json = {} @@ -416,12 +415,12 @@ class Client: snowman = car_json.get("jbm") == "unicycle" if allow and config.Game['cars'] > cars_count or (snowman and allow_snowman) or over_spawn: if snowman: - unicycle_id = self._snowman['id'] + unicycle_id = self._unicycle['id'] if unicycle_id != -1: - self.log.debug(f"Delete old unicycle: unicycle_id={unicycle_id}") + self.log.debug(f"Delete old unicycle: car_id={unicycle_id}") self._cars[unicycle_id] = None await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True) - self._snowman = {"id": car_id, "packet": pkt} + self._unicycle = {"id": car_id, "packet": pkt} self.log.debug(f"Unicycle spawn accepted: car_id={car_id}") else: self.log.debug(f"Car spawn accepted: car_id={car_id}") @@ -469,8 +468,8 @@ class Client: car = self._cars[car_id] if car['snowman']: self.log.debug(f"Snowman found") - unicycle_id = self._snowman['id'] - self._snowman['id'] = -1 + unicycle_id = self._unicycle['id'] + self._unicycle['id'] = -1 self._cars[unicycle_id] = None self._cars[car_id] = None await self._send(f"Od:{self.cid}-{car_id}", to_all=True, to_self=True) @@ -482,7 +481,7 @@ class Client: async def _edit_car(self, raw_data, data): cid, car_id = self._get_cid_vid(raw_data) if car_id != -1 and self._cars[car_id]: - client = self.__Core.get_client(cid=cid) + client = self._core.get_client(cid=cid) if client: car = client._cars[car_id] new_car_json = {} @@ -506,8 +505,8 @@ class Client: if cid == self.cid or allow or admin_allow: if car['snowman']: - unicycle_id = self._snowman['id'] - self._snowman['id'] = -1 + unicycle_id = self._unicycle['id'] + self._unicycle['id'] = -1 self.log.debug(f"Delete snowman") await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True) self._cars[unicycle_id] = None @@ -593,7 +592,7 @@ class Client: await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it await self._send(f"J{i18n.game_welcome_message.format(self.nick)}", to_all=True) # Hello message - for client in self.__Core.clients: + for client in self._core.clients: if not client: continue for car in client._cars: @@ -656,20 +655,18 @@ class Client: self.__alive = False return - # Codes: V W X Y - if 89 >= data[0] >= 86: - await self._send(data, to_all=True, to_self=False) - return - _bytes = False try: data = data.decode() except UnicodeDecodeError: _bytes = True self.log.error(f"UnicodeDecodeError: {data}") - self.log.info("Some things are skipping...") - # Codes: p, Z in udp_server.py + if data[0] in ['V', 'W', 'Y', 'E', 'N']: + await self._send(data, to_all=True, to_self=False) + return + + # Codes: p, Z, X in udp_server.py match data[0]: # At data[0] code case "H": # Map load, client ready await self._connected_handler() @@ -700,8 +697,8 @@ class Client: ev.call_lua_event(event_name, self.cid, even_data) ev.call_event(event_name, data=even_data, player=self) await ev.call_async_event(event_name, data=even_data, player=self) - case "N": - await self._send(data, to_all=True, to_self=False) + case _: + self.log.warning(f"TCP [{self.cid}] Unknown code: {data[0]}; {data}") async def _looper(self): ev.call_lua_event("onPlayerConnecting", self.cid) @@ -728,7 +725,7 @@ class Client: await asyncio.sleep(0.3) self.__alive = False if (self.cid > 0 or self.nick is not None) and \ - self.__Core.clients_by_nick.get(self.nick): + self._core.clients_by_nick.get(self.nick): for i, car in enumerate(self._cars): if not car: continue @@ -746,9 +743,9 @@ class Client: round((time.monotonic() - self._connect_time) / 60, 2) ) ) - self.__Core.clients[self.cid] = None - del self.__Core.clients_by_id[self.cid] - del self.__Core.clients_by_nick[self.nick] + self._core.clients[self.cid] = None + del self._core.clients_by_id[self.cid] + del self._core.clients_by_nick[self.nick] else: self.log.debug(f"Removing client; Closing connection...") try: diff --git a/src/core/Client.pyi b/src/core/Client.pyi index b5cff24..0af0c0b 100644 --- a/src/core/Client.pyi +++ b/src/core/Client.pyi @@ -25,7 +25,7 @@ class Client: self._log = utils.get_logger("client(id: )") self._addr: Tuple[str, int] = writer.get_extra_info("sockname") self._loop = asyncio.get_event_loop() - self.__Core: Core = core + self._core: Core = core self._cid: int = -1 self._key: str = None self.nick: str = None @@ -37,7 +37,7 @@ class Client: self._focus_car = -1 self._identifiers = [] self._cars: List[Union[Dict[str, Union[str, bool, Dict[str, Union[str, List[int], float]]]], None]] = [] - self._snowman: Dict[str, Union[int, str]] = {"id": -1, "packet": ""} + self._unicycle: Dict[str, Union[int, str]] = {"id": -1, "packet": ""} self._last_position = {} self._lock = Lock() diff --git a/src/core/__init__.py b/src/core/__init__.py index 05b0366..f1750bc 100644 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -9,12 +9,12 @@ __title__ = 'KuiToi-Server' __description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.' __url__ = 'https://github.com/kuitoi/kuitoi-Server' -__version__ = '0.4.5' -__build__ = 2303 # Я это считаю лог файлами +__version__ = '0.4.6' +__build__ = 2421 # Я это считаю лог файлами __author__ = 'SantaSpeen' __author_email__ = 'admin@kuitoi.su' __license__ = "FPA" -__copyright__ = 'Copyright 2023 © SantaSpeen (Maxim Khomutov)' +__copyright__ = 'Copyright 2024 © SantaSpeen (Maxim Khomutov)' import asyncio import builtins @@ -43,7 +43,7 @@ config_path = "kuitoi.yml" if args.config: config_path = args.config config_provider = ConfigProvider(config_path) -config = config_provider.open_config() +config = config_provider.read() builtins.config = config config.enc = config.Options['encoding'] if config.Options['debug'] is True: @@ -60,6 +60,7 @@ ml.builtins_hook() log.debug("Initializing EventsSystem...") ev = EventsSystem() ev.builtins_hook() +ev.register("get_version", lambda _: {"version": __version__, "build": __build__}) log.info(i18n.hello) log.info(i18n.config_path.format(config_path)) @@ -68,7 +69,7 @@ log.debug("Initializing BeamMP Server system...") # Key handler.. if not config.Auth['private'] and not config.Auth['key']: log.warn(i18n.auth_need_key) - url = "https://beammp.com/k/keys" + url = "https://keymaster.beammp.com/login" if shortcuts.yes_no_dialog( title='BeamMP Server Key', text=i18n.GUI_need_key_message, @@ -90,7 +91,7 @@ if not config.Auth['private'] and not config.Auth['key']: text=i18n.GUI_enter_key_message, ok_text=i18n.GUI_ok, cancel_text=i18n.GUI_cancel).run() - config_provider.save_config() + config_provider.save() if not config.Auth['private'] and not config.Auth['key']: log.error(i18n.auth_empty_key) log.info(i18n.stop) diff --git a/src/core/core.py b/src/core/core.py index 1ff0b64..51cc7ae 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -27,7 +27,8 @@ class Core: def __init__(self): self.log = utils.get_logger("core") - self.loop = asyncio.get_event_loop() + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) self.start_time = time.monotonic() self.run = False self.direct = False @@ -47,7 +48,7 @@ class Core: self.lock_upload = False self.client_major_version = "2.0" - self.BeamMP_version = "3.4.1" # 20.07.2023 + self.BeamMP_version = "3.4.1" # 16.07.2024 ev.register("_get_BeamMP_version", lambda x: tuple([int(i) for i in self.BeamMP_version.split(".")])) ev.register("_get_player", lambda x: self.get_client(**x['kwargs'])) @@ -103,6 +104,7 @@ class Core: return out async def check_alive(self): + self.log.debug("Starting alive checker.") maxp = config.Game['players'] try: while self.run: @@ -138,6 +140,8 @@ class Core: uvserver.run() async def stop_me(self): + if not config.WebAPI['enabled']: + return while webapp.data_run[0]: await asyncio.sleep(1) self.run = False @@ -145,31 +149,44 @@ class Core: # noinspection SpellCheckingInspection,PyPep8Naming async def heartbeat(self, test=False): - if config.Auth["private"] or self.direct: - if test: - self.log.info(i18n.core_direct_mode) - self.direct = True - return + try: + self.log.debug("Starting heartbeat.") + if config.Auth["private"] or self.direct: + if test: + self.log.info(i18n.core_direct_mode) + self.direct = True + return - BEAM_backend = ["backend.beammp.com", "backup1.beammp.com", "backup2.beammp.com"] - modlist = "" - for mod in self.mods_list: - if type(mod) == int: - continue - modlist += f"/{os.path.basename(mod['path'])};" - modstotalsize = self.mods_list[0] - modstotal = len(self.mods_list) - 1 - while self.run: - try: - data = {"uuid": config.Auth["key"], "players": len(self.clients_by_id), - "maxplayers": config.Game["players"], "port": config.Server["server_port"], - "map": f"/levels/{config.Game['map']}/info.json", "private": config.Auth['private'], - "version": self.BeamMP_version, "clientversion": self.client_major_version, - "name": config.Server["name"], "modlist": modlist, "modstotalsize": modstotalsize, - "modstotal": modstotal, "playerslist": "", "desc": config.Server['description'], "pass": False} + BEAM_backend = ["backend.beammp.com", "backup1.beammp.com", "backup2.beammp.com"] + _map = config.Game['map'] if "/" in config.Game['map'] else f"/levels/{config.Game['map']}/info.json" + tags = config.Server['tags'].replace(", ", ";").replace(",", ";") + if tags and tags[-1:] != ";": + tags += ";" + modlist = "".join(f"/{os.path.basename(mod['path'])};" for mod in self.mods_list[1:]) + modstotalsize = self.mods_list[0] + modstotal = len(self.mods_list) - 1 + while self.run: + playerslist = "".join(f"{client.nick};" for client in self.clients if client and client.alive) + data = { + "uuid": config.Auth["key"], + "players": len(self.clients_by_id), + "maxplayers": config.Game["players"], + "port": config.Server["server_port"], + "map": _map, + "private": config.Auth['private'], + "version": self.BeamMP_version, + "clientversion": self.client_major_version, + "name": config.Server["name"], + "tags": tags, + "guests": not config.Auth["private"], + "modlist": modlist, + "modstotalsize": modstotalsize, + "modstotal": modstotal, + "playerslist": playerslist, + "desc": config.Server['description'], + "pass": False + } - # Sentry? - ok = False body = {} for server_url in BEAM_backend: url = "https://" + server_url + "/heartbeat" @@ -177,14 +194,15 @@ class Core: async with aiohttp.ClientSession() as session: async with session.post(url, data=data, headers={"api-v": "2"}) as response: code = response.status + # text = await response.text() + # self.log.debug(f"[HB] res={text}") body = await response.json() - ok = True break except Exception as e: self.log.debug(f"Auth: Error `{e}` while auth with `{server_url}`") continue - if ok: + if body: if not (body.get("status") is not None and body.get("code") is not None and body.get("msg") is not None): @@ -216,11 +234,11 @@ class Core: # raise KeyboardInterrupt if test: - return ok + return bool(body) await asyncio.sleep(5) - except Exception as e: - self.log.error(f"Error in heartbeat: {e}") + except Exception as e: + self.log.error(f"Error in heartbeat: {e}") async def kick_cmd(self, args): if not len(args) > 0: diff --git a/src/core/tcp_server.py b/src/core/tcp_server.py index dddcf67..5d6473c 100644 --- a/src/core/tcp_server.py +++ b/src/core/tcp_server.py @@ -94,9 +94,8 @@ class TCPServer: await client.kick(i18n.core_player_kick_server_full) return False, client else: - self.log.info(i18n.core_identifying_okay) await self.Core.insert_client(client) - client.log.info(i18n.core_player_set_id.format(client.pid)) + client.log.info(i18n.core_identifying_okay) return True, client @@ -137,7 +136,8 @@ class TCPServer: try: ip = writer.get_extra_info('peername')[0] if self.rl.is_banned(ip): - self.rl.notify(ip, writer) + await self.rl.notify(ip, writer) + writer.close() break data = await reader.read(1) if not data: diff --git a/src/core/udp_server.py b/src/core/udp_server.py index 9240f52..92639a4 100644 --- a/src/core/udp_server.py +++ b/src/core/udp_server.py @@ -18,7 +18,7 @@ class UDPServer(asyncio.DatagramTransport): super().__init__() self.log = utils.get_logger("UDPServer") self.loop = asyncio.get_event_loop() - self.Core = core + self._core = core self.host = host self.port = port self.run = False @@ -33,8 +33,10 @@ class UDPServer(asyncio.DatagramTransport): code = data[2:3].decode() data = data[2:].decode() - client = self.Core.get_client(cid=cid) + client = self._core.get_client(cid=cid) if client: + if not client.alive: + self.log.debug(f"{client.nick}:{cid} still sending UDP data: {data}") match code: case "p": # Ping packet ev.call_event("onSentPing") @@ -45,24 +47,26 @@ class UDPServer(asyncio.DatagramTransport): self.log.debug(f"Set UDP Sock for CID: {cid}") ev.call_event("onChangePosition", data=data) sub = data.find("{", 1) - last_pos_data = data[sub:] + last_pos = data[sub:] try: - last_pos = json.loads(last_pos_data) - client._last_position = last_pos _, car_id = client._get_cid_vid(data) - client._cars[car_id]['pos'] = last_pos + if client._cars[car_id]: + last_pos = json.loads(last_pos) + client._last_position = last_pos + client._cars[car_id]['pos'] = last_pos except Exception as e: - self.log.debug(f"Cannot parse position packet: {e}") - self.log.debug(f"data: {data}, sup: {sub}") - self.log.debug(f"last_pos_data: {last_pos_data}") + self.log.warning(f"Cannot parse position packet: {e}") + self.log.debug(f"data: '{data}', sup: {sub}") + self.log.debug(f"last_pos ({type(last_pos)}): {last_pos}") + await client._send(data, to_all=True, to_self=False, to_udp=True) + case "X": await client._send(data, to_all=True, to_self=False, to_udp=True) case _: - self.log.debug(f"[{cid}] Unknown code: {code}") + self.log.warning(f" UDP [{cid}] Unknown code: {code}; {data}") else: self.log.debug(f"[{cid}] Client not found.") except Exception as e: - self.log.error(f"Error handle_datagram: {e}") def datagram_received(self, *args, **kwargs): @@ -81,14 +85,14 @@ class UDPServer(asyncio.DatagramTransport): async def _start(self): self.log.debug("Starting UDP server.") - while self.Core.run: + while self._core.run: try: await asyncio.sleep(0.2) d = UDPServer self.transport, p = await self.loop.create_datagram_endpoint( - lambda: d(self.Core), + lambda: d(self._core), local_addr=(self.host, self.port) ) d.transport = self.transport diff --git a/src/core/udp_server.pyi b/src/core/udp_server.pyi index 2d31885..f9cb6ef 100644 --- a/src/core/udp_server.pyi +++ b/src/core/udp_server.pyi @@ -18,7 +18,7 @@ class UDPServer(asyncio.DatagramTransport): def __init__(self, core: Core, host=None, port=None, transport=None): self.log = utils.get_logger("UDPServer") self.loop = asyncio.get_event_loop() - self.Core = core + self._core = core self.host = host self.port = port self.run = False diff --git a/src/modules/ConfigProvider/__init__.py b/src/modules/ConfigProvider/__init__.py index cdcc56a..32b3ea6 100644 --- a/src/modules/ConfigProvider/__init__.py +++ b/src/modules/ConfigProvider/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +import copy # Developed by KuiToi Dev # File modules.ConfigProvider # Written by: SantaSpeen @@ -16,12 +16,12 @@ class Config: def __init__(self, auth=None, game=None, server=None, rcon=None, options=None, web=None): self.Auth = auth or {"key": None, "private": True} self.Game = game or {"map": "gridmap_v2", "players": 8, "cars": 1} - self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", + self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", "tags": "Freroam", "server_ip": "0.0.0.0", "server_port": 30814} + self.Options = options or {"language": "en", "speed_limit": 0, "use_queue": False, + "use_lua": False, "log_chat": True} self.RCON = rcon or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 10383, - "password": secrets.token_hex(16)} - self.Options = options or {"language": "en", "encoding": "utf-8", "speed_limit": 0, "use_queue": False, - "debug": False, "use_lua": False, "log_chat": True} + "password": secrets.token_hex(6)} self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433, "access_token": secrets.token_hex(16)} @@ -36,7 +36,7 @@ class ConfigProvider: self.config_path = config_path self.config = Config() - def open_config(self): + def read(self, _again=False): if not os.path.exists(self.config_path): with open(self.config_path, "w", encoding="utf-8") as f: yaml.dump(self.config, f) @@ -47,9 +47,26 @@ class ConfigProvider: print("You have errors in the YAML syntax.") print("Stopping server.") exit(1) + if not self.config: + if _again: + print("Error: empty configuration.") + exit(1) + print("Empty config?..") + os.remove(self.config_path) + self.config = Config() + return self.read(True) + + if not self.config.Options.get("debug"): + self.config.Options['debug'] = False + if not self.config.Options.get("encoding"): + self.config.Options['encoding'] = "utf-8" return self.config - def save_config(self): + def save(self): + _config = copy.deepcopy(self.config) + del _config.enc + del _config.Options['debug'] + del _config.Options['encoding'] with open(self.config_path, "w", encoding="utf-8") as f: - yaml.dump(self.config, f) + yaml.dump(_config, f) diff --git a/src/modules/RateLimiter/__init__.py b/src/modules/RateLimiter/__init__.py index ac94717..453b4eb 100644 --- a/src/modules/RateLimiter/__init__.py +++ b/src/modules/RateLimiter/__init__.py @@ -40,6 +40,7 @@ class RateLimiter: if len(x) == 2: ip = x[1] if ip in _banned_ips: + self._notified[ip] = False self._calls[ip].clear() self._banned_until[ip] = datetime.now() return f"{ip} removed from banlist." @@ -52,6 +53,7 @@ class RateLimiter: sec = x[2] if not sec.isdigit(): return f"{sec!r} is not digit." + self._notified[ip] = False self._calls[ip].clear() self._banned_until[ip] = datetime.now() + timedelta(seconds=int(sec)) return f"{ip} banned until {self._banned_until[ip]}" @@ -69,7 +71,6 @@ class RateLimiter: try: writer.write(b'\x0b\x00\x00\x00Eip banned.') await writer.drain() - writer.close() except Exception: pass diff --git a/src/modules/i18n/readme.md b/src/modules/i18n/readme.md index 5509298..55af972 100644 --- a/src/modules/i18n/readme.md +++ b/src/modules/i18n/readme.md @@ -45,7 +45,6 @@ class i18n: core_player_kick_stale: str core_player_kick_no_allowed_default_reason: str core_player_kick_server_full: str - core_player_set_id: str core_identifying_okay: str # In-game phrases @@ -110,5 +109,4 @@ class i18n: # Command: exit man_message_exit: str help_message_exit: str - ``` \ No newline at end of file diff --git a/src/translates/cn.json b/src/translates/cn.json index 5b232a0..b9c8e86 100644 --- a/src/translates/cn.json +++ b/src/translates/cn.json @@ -41,7 +41,6 @@ "core_player_kick_stale": "过时的客户端。(由新连接替换)", "core_player_kick_no_allowed_default_reason": "您不受欢迎。拒绝访问。", "core_player_kick_server_full": "服务器已满。", - "core_player_set_id": "玩家设置ID {}", "core_identifying_okay": "成功登录。", "": "游戏内短语", diff --git a/src/translates/en.json b/src/translates/en.json index a6f64da..b35373b 100644 --- a/src/translates/en.json +++ b/src/translates/en.json @@ -41,7 +41,6 @@ "core_player_kick_stale": "Stale client. (Replaced by new connection)", "core_player_kick_no_allowed_default_reason": "You are not welcome on this server. Access denied.", "core_player_kick_server_full": "Server is full.", - "core_player_set_id": "Player set ID {}", "core_identifying_okay": "Successful login.", "": "In-game phrases", diff --git a/src/translates/ru.json b/src/translates/ru.json index 7ea766c..59215ec 100644 --- a/src/translates/ru.json +++ b/src/translates/ru.json @@ -41,7 +41,6 @@ "core_player_kick_stale": "Устаревший клиент. (Заменено новым подключением)", "core_player_kick_no_allowed_default_reason": "Вам не рады на этом сервере. Вход запрещён.", "core_player_kick_server_full": "Сервер полон.", - "core_player_set_id": "Игрок получил ID {}", "core_identifying_okay": "Успешный вход.", "": "In-game phrases",