mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 08:15:42 +00:00
Compare commits
3 Commits
7466f987ac
...
719e705bab
Author | SHA1 | Date | |
---|---|---|---|
719e705bab | |||
21dd23cb55 | |||
9295ed2b7a |
@ -9,3 +9,4 @@ click~=8.1.4
|
||||
lupa~=2.0
|
||||
toml~=0.10.2
|
||||
colorama~=0.4.6
|
||||
cryptography~=41.0.2
|
@ -561,7 +561,7 @@ class Client:
|
||||
await ev.call_async_event("onPlayerJoin", player=self)
|
||||
|
||||
await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it
|
||||
await self._send(i18n.game_welcome_message.format(self.nick), to_all=True) # Hello message
|
||||
await self._send(f"J{i18n.game_welcome_message.format(self.nick)}", to_all=True) # Hello message
|
||||
|
||||
for client in self.__Core.clients:
|
||||
if not client:
|
||||
|
@ -270,6 +270,9 @@ class Core:
|
||||
tasks = []
|
||||
# self.udp.start,
|
||||
f_tasks = [self.tcp.start, self.udp._start, console.start, self.stop_me, self.heartbeat, self.check_alive]
|
||||
if config.RCON['enabled']:
|
||||
rcon = console.rcon(config.RCON['password'], config.RCON['server_ip'], config.RCON['server_port'])
|
||||
f_tasks.append(rcon.start)
|
||||
for task in f_tasks:
|
||||
tasks.append(asyncio.create_task(task()))
|
||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||
|
@ -1,17 +1,14 @@
|
||||
import secrets
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
||||
self.Auth = auth or {"key": None, "private": True}
|
||||
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
||||
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
||||
"server_ip": "0.0.0.0", "server_port": 30814}
|
||||
self.Options = options or {"language": "en", "encoding": "utf8", "speed_limit": 0, "use_queue": False,
|
||||
"debug": False}
|
||||
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
||||
"secret_key": secrets.token_hex(16)}
|
||||
|
||||
Auth: Dict[str, object]
|
||||
Game: Dict[str, object]
|
||||
Server: Dict[str, object]
|
||||
RCON: Dict[str, object]
|
||||
Options: Dict[str, object]
|
||||
WebAPI: Dict[str, object]
|
||||
enc: str | None
|
||||
def __repr__(self):
|
||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||
class config (Config): ...
|
||||
|
@ -12,15 +12,17 @@ import yaml
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
||||
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, "max_cars": 1}
|
||||
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
||||
"server_ip": "0.0.0.0", "server_port": 30814}
|
||||
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}
|
||||
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
||||
"secret_key": secrets.token_hex(16)}
|
||||
"access_token": secrets.token_hex(16)}
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||
|
92
src/modules/ConsoleSystem/RCON.py
Normal file
92
src/modules/ConsoleSystem/RCON.py
Normal file
@ -0,0 +1,92 @@
|
||||
import hashlib
|
||||
import os
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from core import get_logger
|
||||
|
||||
"""
|
||||
shared key: SHA256 of "password"
|
||||
<header>: "\x00\x00\x00\x00" (Byte order: Little Endian) - like you use
|
||||
<iv>: A set of random bytes packed in base64 (New for each message)
|
||||
-> To server
|
||||
<- From server
|
||||
|
||||
Open TCP connection /
|
||||
| -> "<iv>:hello" Without header, immediately with AES encryption (shared key)
|
||||
| *Decrypt and some processes*
|
||||
| Fail /
|
||||
| | <- ":E:Bad key" | ":E:Error Message" Without header, without AES encryption
|
||||
| | tcp.close() # End
|
||||
| Success /
|
||||
| | <- "<iv>:hello" with header, with AES encryption
|
||||
| | (Next, everywhere with header, with AES encryption)
|
||||
| -> "<iv>:<header>Cs:ver"
|
||||
| <- "<iv>:<header>Os:KuiToi 0.4.3 | "<iv>:<header>Os:BeamMP 3.2.0"
|
||||
| # Prints server and they version
|
||||
| -> "<iv>:<header>Cs:commands"
|
||||
| <- "<iv>:<header>Os:stop,help,plugins" | "<iv>:<header>Os:SKIP" For an autocomplete; "SKIP" For no autocomplete;
|
||||
| *Ready to handle commands*
|
||||
| -> "<iv>:<header>C:help"
|
||||
| <- "<iv>:<header>O:stop: very cool stop\nhelp: Yayayayoy"
|
||||
| -> "<iv>:<header>C:...."
|
||||
| <- "<iv>:<header>O:...."
|
||||
| -> "<iv>:<header>C:exit"
|
||||
| tcp.close()
|
||||
|
||||
Codes:
|
||||
* "hello" - Hello message
|
||||
* "E:error_message" - Send RCON error
|
||||
* "C:command" - Receive command
|
||||
* "Cs:" - Receive system command
|
||||
* "O:output" - Send command output
|
||||
* "Os:" - Send system output
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class RCONSystem:
|
||||
console = None
|
||||
|
||||
def __init__(self, key, host, port):
|
||||
self.log = get_logger("RCON")
|
||||
self.key = key
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
def encrypt(self, message, key):
|
||||
self.log.debug(f"Encrypt message: {message}")
|
||||
key = hashlib.sha256(key).digest()
|
||||
iv = os.urandom(16)
|
||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||
encryptor = cipher.encryptor()
|
||||
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||
padded_data = padder.update(message.encode('utf-8')) + padder.finalize()
|
||||
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
||||
encoded_data = b64encode(encrypted_data)
|
||||
encoded_iv = b64encode(iv)
|
||||
return encoded_iv + b":" + encoded_data
|
||||
|
||||
def decrypt(self, ciphertext, key):
|
||||
self.log.debug(f"Dencrypt message: {ciphertext}")
|
||||
key = hashlib.sha256(key).digest()
|
||||
encoded_iv, encoded_data = ciphertext.split(":")
|
||||
iv = b64decode(encoded_iv)
|
||||
encrypted_data = b64decode(encoded_data)
|
||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||
decryptor = cipher.decryptor()
|
||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
|
||||
return unpadded_data.decode('utf-8')
|
||||
|
||||
async def handle_client(self):
|
||||
pass
|
||||
|
||||
async def start(self):
|
||||
self.log.info("TODO: RCON")
|
||||
|
||||
async def stop(self):
|
||||
pass
|
@ -1,36 +1,34 @@
|
||||
class Console(object):
|
||||
from logging import Logger
|
||||
from typing import AnyStr
|
||||
|
||||
def __init__(self,
|
||||
prompt_in: str = ">",
|
||||
prompt_out: str = "]:",
|
||||
not_found: str = "Command \"%s\" not found in alias.") -> None: ...
|
||||
from core import get_logger
|
||||
|
||||
def __getitem__(self, item): ...
|
||||
@property
|
||||
def alias(self) -> dict: ...
|
||||
def add(self, key: str, func: function) -> dict: ...
|
||||
def log(self, s: str, r='\r') -> None: ...
|
||||
def write(self, s: str, r='\r') -> None: ...
|
||||
def __lshift__(self, s: AnyStr) -> None: ...
|
||||
def logger_hook(self) -> None: ...
|
||||
def builtins_hook(self) -> None: ...
|
||||
async def start(self) -> None: ...
|
||||
|
||||
class console(object):
|
||||
class RCONSystem:
|
||||
console = None
|
||||
|
||||
def __init__(self, key, host, port):
|
||||
self.log = get_logger("RCON")
|
||||
self.key = key
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
async def start(self): ...
|
||||
async def stop(self): ...
|
||||
|
||||
class console:
|
||||
rcon: RCONSystem = RCONSystem
|
||||
|
||||
@staticmethod
|
||||
def alias() -> dict: ...
|
||||
@staticmethod
|
||||
def add_command(key: str, func: function) -> dict: ...
|
||||
|
||||
def add_command(key: str, func, man: str = None, desc: str = None, custom_completer: dict = None) -> dict: ...
|
||||
@staticmethod
|
||||
async def start() -> None: ...
|
||||
|
||||
@staticmethod
|
||||
def builtins_hook() -> None: ...
|
||||
@staticmethod
|
||||
def logger_hook() -> None: ...
|
||||
|
||||
@staticmethod
|
||||
def log(s: str) -> None: ...
|
||||
@staticmethod
|
||||
|
@ -18,6 +18,7 @@ from prompt_toolkit.output.win32 import NoConsoleScreenBufferError
|
||||
from prompt_toolkit.patch_stdout import patch_stdout
|
||||
|
||||
from core import get_logger
|
||||
from modules.ConsoleSystem import RCON
|
||||
|
||||
|
||||
class Console:
|
||||
@ -45,6 +46,9 @@ class Console:
|
||||
self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help,
|
||||
custom_completer={"help": {"--raw": None}})
|
||||
self.completer = NestedCompleter.from_nested_dict(self.__alias)
|
||||
rcon = RCON
|
||||
rcon.console = self
|
||||
self.rcon = rcon
|
||||
|
||||
def __debug(self, *x):
|
||||
self.__logger.debug(f"{x}")
|
||||
|
@ -580,13 +580,10 @@ class LuaPluginsLoader:
|
||||
|
||||
def load(self):
|
||||
self.log.debug("Loading Lua plugins...")
|
||||
# TODO: i18n
|
||||
self.log.info("You have enabled support for Lua plugins.")
|
||||
self.log.warning("There are some nuances to working with KuiToi. "
|
||||
"If you have a proposal for their solution, and it is related to KuiToi, "
|
||||
"please contact the developer.")
|
||||
self.log.warning("Some BeamMP plugins require a correctly configured ServerConfig.toml file to function.")
|
||||
self.log.info("Creating it.")
|
||||
self.log.info(i18n.plugins_lua_enabled)
|
||||
self.log.warning(i18n.plugins_lua_nuances_warning)
|
||||
self.log.warning(i18n.plugins_lua_legacy_config_create_warning)
|
||||
self.log.info(i18n.plugins_lua_legacy_config_create)
|
||||
data = {
|
||||
"info": "ServerConfig.toml is created solely for backward compatibility support. "
|
||||
"This file will be updated every time the program is launched.",
|
||||
@ -674,6 +671,6 @@ class LuaPluginsLoader:
|
||||
self.log.debug("Unloading lua plugins")
|
||||
for name, data in self.lua_plugins.items():
|
||||
if data['ok']:
|
||||
self.log.info(f"Unloading lua plugin: {name}")
|
||||
self.log.info(i18n.plugins_lua_unload.format(name))
|
||||
for _, timer in data['lua'].globals().MP._event_timers.items():
|
||||
timer.stop()
|
||||
|
@ -131,21 +131,21 @@ class PluginsLoader:
|
||||
try:
|
||||
is_func = inspect.isfunction
|
||||
if not is_func(plugin.load):
|
||||
self.log.error('Function "def load():" not found.')
|
||||
self.log.error(i18n.plugins_not_found_load)
|
||||
ok = False
|
||||
if not is_func(plugin.start):
|
||||
self.log.error('Function "def start():" not found.')
|
||||
self.log.error(i18n.plugins_not_found_start)
|
||||
ok = False
|
||||
if not is_func(plugin.unload):
|
||||
self.log.error('Function "def unload():" not found.')
|
||||
self.log.error(i18n.plugins_not_found_unload)
|
||||
ok = False
|
||||
if type(plugin.kt) != KuiToi:
|
||||
self.log.error(f'Attribute "kt" isn\'t KuiToi class. Plugin file: "{file_path}"')
|
||||
self.log.error(i18n.plugins_kt_invalid)
|
||||
ok = False
|
||||
except AttributeError:
|
||||
ok = False
|
||||
if not ok:
|
||||
self.log.error(f'Plugin file: "{file_path}" is not a valid KuiToi plugin.')
|
||||
self.log.error(i18n.plugins_invalid.format(file_path))
|
||||
return
|
||||
|
||||
pl_name = plugin.kt.name
|
||||
@ -185,9 +185,8 @@ class PluginsLoader:
|
||||
self.loaded_str += f"{pl_name}:ok, "
|
||||
self.log.debug(f"Plugin loaded: {file}. Settings: {self.plugins[pl_name]}")
|
||||
except Exception as e:
|
||||
# TODO: i18n
|
||||
self.loaded_str += f"{file}:no, "
|
||||
self.log.error(f"Error while loading plugin: {file}; Error: {e}")
|
||||
self.log.error(i18n.plugins_error_loading.format(file, f"{e}"))
|
||||
self.log.exception(e)
|
||||
|
||||
async def start(self, _):
|
||||
|
@ -57,6 +57,25 @@
|
||||
"client_event_invalid_data": "从事件返回的数据无效:{}",
|
||||
"client_player_disconnected": "离开服务器。游戏时间:{}分钟。",
|
||||
|
||||
"": "Events system",
|
||||
|
||||
"": "插件加载器",
|
||||
|
||||
"plugins_not_found_load": "未找到\"def load():\"函数。",
|
||||
"plugins_not_found_start": "未找到\"def start():\"函数。",
|
||||
"plugins_not_found_unload": "未找到\"def unload():\"函数。",
|
||||
"plugins_kt_invalid": "“kt”变量不属于KuiToi类。",
|
||||
"plugins_invalid": "无法在KuiToi中运行插件\"{}\"。",
|
||||
"plugins_error_loading": "加载插件{}时出错:{}",
|
||||
|
||||
"": "Lua插件加载器",
|
||||
|
||||
"plugins_lua_enabled": "您已启用Lua插件支持。",
|
||||
"plugins_lua_nuances_warning": "在使用KuiToi时有一些细微差别。如果您有关于解决方案的建议,并且它与KuiToi相关,请联系开发人员。",
|
||||
"plugins_lua_legacy_config_create_warning": "一些BeamMP插件需要一个正确配置的ServerConfig.toml文件才能正常运行。",
|
||||
"plugins_lua_legacy_config_create": "正在创建。",
|
||||
"plugins_lua_unload": "停止Lua插件:{}",
|
||||
|
||||
"": "命令:man",
|
||||
"man_message_man": "man - 显示COMMAND的帮助页面。\n用法:man COMMAND",
|
||||
"help_message_man": "显示COMMAND的帮助页面。",
|
||||
|
@ -57,6 +57,25 @@
|
||||
"client_event_invalid_data": "Invalid data returned from event: {}",
|
||||
"client_player_disconnected": "Left the server. Playtime: {} min",
|
||||
|
||||
"": "Events system",
|
||||
|
||||
"": "Plugins loader",
|
||||
|
||||
"plugins_not_found_load": "Function \"def load():\" not found.",
|
||||
"plugins_not_found_start": "Function \"def start():\" not found.",
|
||||
"plugins_not_found_unload": "Function \"def unload():\" not found.",
|
||||
"plugins_kt_invalid": "\"kt\" variable does not belong to the KuiToi class.",
|
||||
"plugins_invalid": "Plugin \"{}\" cannot be run in KuiToi.",
|
||||
"plugins_error_loading": "An error occurred while loading the plugin {}: {}",
|
||||
|
||||
"": "Lua plugins loader",
|
||||
|
||||
"plugins_lua_enabled": "You have enabled Lua plugin support.",
|
||||
"plugins_lua_nuances_warning": "There are some nuances when working with Kuiti. If you have a suggestion for their solution, and it is related to KuiToi, please contact the developer.",
|
||||
"plugins_lua_legacy_config_create_warning": "Some BeamMP plugins require a properly configured ServerConfig.toml file to function.",
|
||||
"plugins_lua_legacy_config_create": "Creating it.",
|
||||
"plugins_lua_unload": "Stopping Lua plugin: {}",
|
||||
|
||||
"": "Command: man",
|
||||
"man_message_man": "man - Shows the help page for COMMAND.\nUsage: man COMMAND",
|
||||
"help_message_man": "Shows the help page for COMMAND.",
|
||||
|
@ -57,6 +57,25 @@
|
||||
"client_event_invalid_data": "Из ивента вернулись не верные данные: {}",
|
||||
"client_player_disconnected": "Вышел с сервера. Время игры: {} мин",
|
||||
|
||||
"": "Events system",
|
||||
|
||||
"": "Plugins loader",
|
||||
|
||||
"plugins_not_found_load": "Функция \"def load():\" не найдена.",
|
||||
"plugins_not_found_start": "Функция \"def start():\" не найдена.",
|
||||
"plugins_not_found_unload": "Функция \"def unload():\" не найдена.",
|
||||
"plugins_kt_invalid": "Переменная \"kt\" не принадлежит классу KuiToi.",
|
||||
"plugins_invalid": "Плагин: \"{}\" - не может быть запущен в KuiToi.",
|
||||
"plugins_error_loading": "Произошла ошибка при загрузке плагина {}: {}",
|
||||
|
||||
"": "Lua plugins loader",
|
||||
|
||||
"plugins_lua_enabled": "Вы включили поддержку плагинов Lua.",
|
||||
"plugins_lua_nuances_warning": "В работе с Kuiti есть некоторые нюансы. Если у вас есть предложение по их решению, и оно связано с KuiToi, пожалуйста, свяжитесь с разработчиком.",
|
||||
"plugins_lua_legacy_config_create_warning": "Для работы некоторых плагинов BeamMP требуется правильно настроенный файл ServerConfig.toml.",
|
||||
"plugins_lua_legacy_config_create": "Создаю его.",
|
||||
"plugins_lua_unload": "Останавливаю Lua плагин: {}",
|
||||
|
||||
"": "Command: man",
|
||||
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
|
||||
"help_message_man": "Показывает страничку помощи для COMMAND.",
|
||||
|
@ -60,6 +60,27 @@ class i18n:
|
||||
client_event_invalid_data: str
|
||||
client_player_disconnected: str
|
||||
|
||||
# Events system
|
||||
|
||||
|
||||
|
||||
# Plugins loader
|
||||
|
||||
plugins_not_found_load: str
|
||||
plugins_not_found_start: str
|
||||
plugins_not_found_unload: str
|
||||
plugins_kt_invalid: str
|
||||
plugins_invalid: str
|
||||
plugins_error_loading: str
|
||||
|
||||
# Lua plugins loader
|
||||
|
||||
plugins_lua_enabled: str
|
||||
plugins_lua_nuances_warning: str
|
||||
plugins_lua_legacy_config_create_warning: str
|
||||
plugins_lua_legacy_config_create: str
|
||||
plugins_lua_unload: str
|
||||
|
||||
# Command: man
|
||||
man_message_man: str
|
||||
help_message_man: str
|
||||
|
Loading…
x
Reference in New Issue
Block a user