Compare commits

...

10 Commits

Author SHA1 Message Date
9b00552912 Add new position in config 2023-07-11 01:14:30 +03:00
087e416bf2 Add pip release 2023-07-11 01:03:11 +03:00
5ea95aaa39 BEAMP -> KuiToi 2023-07-11 00:34:56 +03:00
67b5142687 Add web API docs 2023-07-11 00:22:54 +03:00
f6c1d64e2b Update version 2023-07-11 00:22:35 +03:00
bf4003e2c7 Update i18n support
Add new blocks
Add docs
2023-07-10 23:49:25 +03:00
3be544eab2 Add Russian 2023-07-10 23:48:04 +03:00
7472aaf206 Update TODOs 2023-07-10 23:16:31 +03:00
6367f1265d Update TODOs 2023-07-10 22:10:56 +03:00
71b6e7d9a4 Update TODOs 2023-07-08 19:00:46 +03:00
30 changed files with 612 additions and 122 deletions

View File

@ -7,14 +7,17 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
## TODOs ## TODOs
- [ ] Server core - [ ] Server core
- [ ] BEAMP System
- [x] Private access without key
- [ ] Server authentication (For public access)
- [X] Player authentication
- [ ] TCP Server part: - [ ] TCP Server part:
- [x] Handle code - [x] Handle code
- [x] Understanding beamp header - [x] Understanding beamp header
- [X] Authorization
- [ ] Upload mods - [ ] Upload mods
- [x] Connecting to the world - [x] Connecting to the world
- [x] Chat - [x] Chat
- [ ] ABG: (compressed data) - [ ] "ABG:" (compressed data)
- [x] Decompress data - [x] Decompress data
- [ ] Vehicle data - [ ] Vehicle data
- [ ] Players synchronizations - [ ] Players synchronizations
@ -26,10 +29,12 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [x] Events System - [x] Events System
- [x] Plugins support - [x] Plugins support
- [x] MultiLanguage (i18n support) - [x] MultiLanguage (i18n support)
- [ ] HTTP REST API Server
- [ ] Console: - [ ] Console:
- [x] Tabulation - [x] Tabulation
- [ ] _(Deferred)_ Normal text scroll - [ ] _(Deferred)_ Normal text scroll
- [x] [Documentation](docs/readme.md) - [x] MultiLanguage (i18n support)
- [ ] [Documentation](docs/en/readme.md)
## Installation ## Installation

View File

@ -0,0 +1,45 @@
{
"": "Basic phases",
"hello": "Hello from KuiToi-Server!",
"config_path": "Use {} for config.",
"init_ok": "Initializing ready.",
"start": "Server started!",
"stop": "Goodbye!",
"": "Server auth",
"auth_need_key": "BEAM key needed for starting the server!",
"auth_empty_key": "Key is empty!",
"auth_cannot_open_browser": "Cannot open browser: {}",
"auth_use_link": "Use this link: {}",
"": "GUI phases",
"GUI_yes": "Yes",
"GUI_no": "No",
"GUI_ok": "Ok",
"GUI_cancel": "Cancel",
"GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?",
"GUI_enter_key_message": "Please type your key:",
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
"": "Command: man",
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
"help_message_man": "Display the manual page for COMMAND.",
"man_for": "Manual for command",
"man_message_not_found": "man: Manual message not found.",
"man_command_not_found": "man: command \"{}\" not found!",
"": "Command: help",
"man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.",
"help_message_help": "Display names and brief descriptions of available commands",
"help_command": "Command",
"help_message": "Help message",
"help_message_not_found": "No help message found",
"": "Command: stop",
"man_message_stop": "stop - Just shutting down the server.\nUsage: stop",
"help_message_stop": "Server shutdown.",
"": "Command: exit",
"man_message_exit": "exit - Just shutting down the server.\nUsage: stop",
"help_message_exit": "Server shutdown."
}

View File

View File

@ -1,6 +1,6 @@
import BEAMP # Import server object import KuiToi # Import server object
beam = BEAMP("TestPlugin") # Init plugin with name "TestPlugin" beam = KuiToi("TestPlugin") # Init plugin with name "TestPlugin"
log = beam.log # Use logger from server log = beam.log # Use logger from server

View File

@ -1,20 +1,19 @@
# Plugins System # Plugins System
## Install ## Install
###### (Lib can't ready to use)
[//]: # (TODO: BEAM LIB)
Lib can't ready to use
* From pip:\ * From pip:\
`$ pip install ...` `$ pip install KuiToi`
* From source:\ * From source:\
`git clone https://github.com/kuitoi/...` `git clone https://github.com/KuiToi/KuiToi-PyLib`
## Example ## Example
```python ```python
import BEAMP import KuiToi
beam = BEAMP("TestPlugin") beam = KuiToi("TestPlugin")
logger = beam.log logger = beam.log
def load(): # Plugins load from here def load(): # Plugins load from here

9
docs/en/readme.md Normal file
View File

@ -0,0 +1,9 @@
# Documentation for KuiToi Server
#### The documentation has not been perfected yet, but one day it will definitely happen
1. Setup and Start server - [here](setup)
2. Plugins and Events system - [here](plugins)
3. MultiLanguage - [here](./multilanguage)
4. RESP API - [here](./web)
5. Something new

3
docs/en/web/readme.md Normal file
View File

@ -0,0 +1,3 @@
# Web RESP API for the Server
In development...

View File

@ -1,5 +1,4 @@
# Documentation for KuiToi Server # Choose your language
1. Setup and Start server - [here](./setup) ### [English](./en)
2. Plugins and Events system - [here](./plugins) ### [Русский](./ru)
3. Something new

View File

@ -0,0 +1,45 @@
{
"": "Basic phases",
"hello": "Привет из KuiToi-Server!",
"config_path": "Используя {} для настройки.",
"init_ok": "Инициализация окончена.",
"start": "Сервер запущен!",
"stop": "Сервер остановлен!",
"": "Server auth",
"auth_need_key": "Нужен BEAMP ключ для запуска!",
"auth_empty_key": "BEAMP ключ пустой!",
"auth_cannot_open_browser": "Не получилось открыть браузер: {}",
"auth_use_link": "Используй эту ссылку: {}",
"": "GUI phases",
"GUI_yes": "Да",
"GUI_no": "Нет",
"GUI_ok": "Окей",
"GUI_cancel": "Отмена",
"GUI_need_key_message": "Нужен BEAMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
"GUI_enter_key_message": "Пожалуйста введите ключ:",
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
"": "Command: man",
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
"help_message_man": "Показывает страничку помощи для COMMAND.",
"man_for": "Страничка помощи для",
"man_message_not_found": "man: Страничка помощи не найдена.",
"man_command_not_found": "man: Команда \"{}\" не найдена!",
"": "Command: help",
"man_message_help": "help - Показывает названия и краткое описание команд.\nИспользование: help [--raw]\nКоманда `help` выводит список всех доступных команд, и краткое описание для каждой команды.",
"help_message_help": "Показывает названия и краткое описание команд",
"help_command": "Команда",
"help_message": "Текст",
"help_message_not_found": "Нет текста",
"": "Command: stop",
"man_message_stop": "stop - Выключает сервер.\nИспользование: stop",
"help_message_stop": "Выключает сервер.",
"": "Command: exit",
"man_message_exit": "exit - Выключает сервер.\nИспользование: exit",
"help_message_exit": "Выключает сервер."
}

View File

View File

@ -0,0 +1,36 @@
import KuiToi # Import server object
beam = KuiToi("TestPlugin") # Init plugin with name "TestPlugin"
log = beam.log # Use logger from server
def on_load():
# When plugin initialization Server uses plugin.load() to load plugin.
# def load(): is really needed
log.info(beam.name)
# Events handlers
def on_started():
# Simple event handler
log.info("Server starting...")
# Simple event register
beam.register_event("on_started", on_started)
def any_func(data=None):
# Custom event handler
log.info(f"Data from any_func: {data}")
# Create custom event
beam.register_event("my_event", any_func)
# Call custom event
beam.call_event("my_event")
beam.call_event("my_event", "Some data")
# This will be an error since any_func accepts only one argument at the input
beam.call_event("my_event", "Some data", "Some data1")

35
docs/ru/plugins/readme.md Normal file
View File

@ -0,0 +1,35 @@
# Система плагинов
## Установка библиотеки с "Заглушками"
###### (Это значит, что не будет работать без сервера, но IDE подскажет API)
###### (Библиотека ещё в разработке)
* Используя pip:\
`$ pip install KuiToi`
* Из исходников:\
`git clone https://github.com/KuiToi/KuiToi-PyLib`
## Пример
```python
import KuiToi
beam = KuiToi("TestPlugin")
logger = beam.log
def load(): # Plugins load from here
print(beam.name)
def on_started():
logger.info("Server starting...")
beam.register_event("on_started", on_started)
```
Так же более обширный пример можно найти в [example.py](./example.py)
* Базовые ивенты: ['on_started', 'on_auth, 'on_stop']
* Создание своего ивента : `beam.register_event("my_event", my_event_function)`
* Вызов ивента: `beam.call_event("my_event")`
* Вызов ивента с данными: `beam.call_event("my_event", data, data2)`
* Вызовы с указанием переменой _**не поддерживаются**_: `beam.call_event("my_event", data=data)`

9
docs/ru/readme.md Normal file
View File

@ -0,0 +1,9 @@
# Документация для KuiToi Server
### Документация ещё не доведена до совершенства, но однажды обязательно это случится
1. Настройка и Запуск сервера - [тута](./setup)
2. Плагины и Ивент система - [тута](./plugins)
3. Мультиязычность - [тута](./multilanguage)
4. RESP API - [тута](./web)
5. Тут будет что-то ново....

49
docs/ru/setup/readme.md Normal file
View File

@ -0,0 +1,49 @@
# Привет из KuiToi Server
## Что ж, начнём
###### _(Тут команды для linux)_
* Для запуска необходим **Python 3.10.x**! Именно этот, на Python 3.11 не запустится...
* Проверить версию твоего питончика(здесь надо смеяться) можно вот так:
```bash
python3 --version # Python 3.10.6
```
* Клонируем репозиторий и переходим в него
* Устанавливаем всё необходимое
* Далее, используя мой "скриптик" удаляем всё лишнее и перемещаемся к исходникам ядра
```bash
git clone -b Stable https://github.com/kuitoi/KuiToi-Server.git && cd KuiToi-Server
pip install -r requirements.txt
mv ./src/ $HOME/ktsrc/ && rm -rf ./* && mv $HOME/ktsrc/* . && rm -rf $HOME/ktsrc
```
* Вот так можно глянуть инфу о сервер и запустить его:)
```bash
python3 main.py --help # Покажет все доступные команды
python3 main.py # Запустит сервер
```
## Настройка
* После запуска создастся `kuitoi.yaml`
* По умолчанию он выглядит вот так:
```yaml
!!python/object:modules.ConfigProvider.config_provider.Config
Auth:
key: null
private: true
Game:
map: gridmap_v2
max_cars: 1
players: 8
Server:
debug: false
description: This server uses KuiToi!
name: KuiToi-Server
server_ip: 0.0.0.0
server_port: 30814
```
* Если поставить `private: false` и не установить `key`, то сервер запросит BEAMP ключ, без него не запустится.
* Введя BEAMP ключ сервер появится в списке лаунчера.
* Взять ключ можно тут: [https://beammp.com/k/keys](https://beammp.com/k/keys)

3
docs/ru/web/readme.md Normal file
View File

@ -0,0 +1,3 @@
# Web RESP API для сервера
В разработке

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.__init__.py # File core.__init__.py
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 1.1 # Version 1.2
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
# Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai) # Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai)
@ -9,8 +9,8 @@
__title__ = 'KuiToi-Server' __title__ = 'KuiToi-Server'
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.' __description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
__url__ = 'https://github.com/kuitoi/kuitoi-Server' __url__ = 'https://github.com/kuitoi/kuitoi-Server'
__version__ = '0.1.5' __version__ = '0.1.6'
__build__ = 411 __build__ = 458
__author__ = 'SantaSpeen' __author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su' __author_email__ = 'admin@kuitoi.su'
__license__ = "FPA" __license__ = "FPA"
@ -28,7 +28,7 @@ import builtins
import os import os
import webbrowser import webbrowser
from prompt_toolkit.shortcuts import input_dialog, yes_no_dialog import prompt_toolkit.shortcuts as shortcuts
from .utils import get_logger from .utils import get_logger
from modules import ConfigProvider, EventsSystem, PluginsLoader from modules import ConfigProvider, EventsSystem, PluginsLoader
@ -46,42 +46,57 @@ if args.config:
config_path = args.config config_path = args.config
config_provider = ConfigProvider(config_path) config_provider = ConfigProvider(config_path)
config = config_provider.open_config() config = config_provider.open_config()
log.info("Use %s for config." % config_path)
if config.Server['debug'] is True: if config.Server['debug'] is True:
utils.set_debug_status() utils.set_debug_status()
log.info("Getting new logging with DEBUG level!") log.info("Debug enabled!")
log = get_logger("init") log = get_logger("init")
log.debug("Debug mode enabled!") log.debug("Debug mode enabled!")
log.debug("Use %s for config." % config) log.debug(f"Server config: {config}")
# i18n init # i18n init
log.debug("Initializing i18n...") log.debug("Initializing i18n...")
ml = MultiLanguage() ml = MultiLanguage()
ml.set_language(args.language) ml.set_language(args.language or config.Server['language'])
ml.builtins_hook() ml.builtins_hook()
log.info(i18n.hello) log.debug("Initializing EventsSystem...")
ev = EventsSystem() ev = EventsSystem()
ev.builtins_hook() ev.builtins_hook()
# Key handler.. log.info(i18n.hello)
if not config.Auth['key']: log.info(i18n.config_path.format(config_path))
log.warn("Key needed for starting the server!")
url = "https://beammp.com/k/keys"
if yes_no_dialog(
title='BEAMP Server Key',
text='Key needed for starting the server!\n'
'Do you need to open the web link to obtain the key?').run():
webbrowser.open(url, new=2)
config.Auth['key'] = input_dialog( log.debug("Initializing BEAMP Server system...")
# Key handler..
private = ((config.Auth['key'] is None or config.Auth['key'] == "") and config.Auth['private'])
if not private:
log.warn(i18n.auth_need_key)
url = "https://beammp.com/k/keys"
if shortcuts.yes_no_dialog(
title='BEAMP Server Key',
text=i18n.GUI_need_key_message,
yes_text=i18n.GUI_yes,
no_text=i18n.GUI_no).run():
try:
log.debug("Opening browser...")
webbrowser.open(url, new=2)
except Exception as e:
log.error(i18n.auth_cannot_open_browser.format(e))
log.info(i18n.auth_use_link.format(url))
shortcuts.message_dialog(
title='BEAMP Server Key',
text=i18n.GUI_cannot_open_browser.format(url),
ok_text=i18n.GUI_ok).run()
config.Auth['key'] = shortcuts.input_dialog(
title='BEAMP Server Key', title='BEAMP Server Key',
text='Please type your key:').run() text=i18n.GUI_enter_key_message,
ok_text=i18n.GUI_ok,
cancel_text=i18n.GUI_cancel).run()
config_provider.save_config() config_provider.save_config()
if not config.Auth['key']: if not private:
log.error("Key is empty!") log.error(i18n.auth_empty_key)
log.error("Server stopped!") log.info(i18n.stop)
exit(1) exit(1)
builtins.config = config builtins.config = config
@ -90,14 +105,16 @@ log.debug("Initializing console...")
console = Console() console = Console()
console.builtins_hook() console.builtins_hook()
# console.logger_hook() # console.logger_hook()
console.add_command("stop", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") console.add_command("stop", console.stop, i18n.man_message_stop, i18n.help_message_stop)
console.add_command("exit", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") console.add_command("exit", console.stop, i18n.man_message_exit, i18n.help_message_exit)
if not os.path.exists("mods"): if not os.path.exists("mods"):
os.mkdir("mods") os.mkdir("mods")
log.debug("Initializing PluginsLoader...")
if not os.path.exists("plugins"): if not os.path.exists("plugins"):
os.mkdir("plugins") os.mkdir("plugins")
pl = PluginsLoader("plugins") pl = PluginsLoader("plugins")
pl.load_plugins() pl.load_plugins()
@ -107,4 +124,4 @@ builtins.MB = KB * 1024
builtins.GB = MB * 1024 builtins.GB = MB * 1024
builtins.TB = GB * 1024 builtins.TB = GB * 1024
log.info(i18n.init) log.info(i18n.init_ok)

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.core.py # File core.core.py
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 0.1.5 # Version 0.1.6
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import asyncio import asyncio
@ -14,12 +14,13 @@ from .udp_server import UDPServer
class Client: class Client:
def __init__(self, reader, writer): def __init__(self, reader, writer, core):
self.reader = reader self.reader = reader
self.writer = writer self.writer = writer
self.log = utils.get_logger("client(None:0)") self.log = utils.get_logger("client(None:0)")
self.addr = writer.get_extra_info("sockname") self.addr = writer.get_extra_info("sockname")
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
self.Core = core
self.cid = 0 self.cid = 0
self.key = None self.key = None
self.nick = None self.nick = None
@ -137,11 +138,11 @@ class Core:
def __init__(self): def __init__(self):
self.log = utils.get_logger("core") self.log = utils.get_logger("core")
self.loop = asyncio.get_event_loop()
self.clients = {} self.clients = {}
self.clients_counter = 0 self.clients_counter = 0
self.server_ip = config.Server["server_ip"] self.server_ip = config.Server["server_ip"]
self.server_port = config.Server["server_port"] self.server_port = config.Server["server_port"]
self.loop = asyncio.get_event_loop()
self.tcp = TCPServer self.tcp = TCPServer
self.udp = UDPServer self.udp = UDPServer
@ -152,14 +153,16 @@ class Core:
return self.clients.get(sock.getsockname()) return self.clients.get(sock.getsockname())
def insert_client(self, client): def insert_client(self, client):
self.log.debug(f"Inserting client: {client.cid}")
self.clients.update({client.cid: client, client.nick: client}) self.clients.update({client.cid: client, client.nick: client})
def create_client(self, *args, **kwargs): def create_client(self, *args, **kwargs):
cl = Client(*args, **kwargs) client = Client(*args, **kwargs)
self.clients_counter = self.clients_counter + 1 self.clients_counter += 1
cl.id = self.clients_counter client.id = self.clients_counter
cl._update_logger() client._update_logger()
return cl self.log.debug(f"Create client: {client.cid}; clients_counter: {self.clients_counter}")
return client
async def check_alive(self): async def check_alive(self):
await asyncio.sleep(5) await asyncio.sleep(5)
@ -174,7 +177,8 @@ class Core:
self.udp = self.udp(self, self.server_ip, self.server_port) self.udp = self.udp(self, self.server_ip, self.server_port)
tasks = [self.tcp.start(), self.udp.start(), console.start()] # self.check_alive() tasks = [self.tcp.start(), self.udp.start(), console.start()] # self.check_alive()
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
self.log.info(i18n.ready) self.log.info(i18n.start)
# TODO: Server auth
ev.call_event("on_started") ev.call_event("on_started")
await t await t
# while True: # while True:
@ -193,5 +197,5 @@ class Core:
asyncio.run(self.main()) asyncio.run(self.main())
def stop(self): def stop(self):
self.log.info("Goodbye!") self.log.info(i18n.stop)
exit(0) exit(0)

View File

@ -1,25 +1,26 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.core.pyi # File core.core.pyi
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 0.1.2 # Version 0.1.6
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import asyncio import asyncio
import socket from asyncio import StreamWriter, StreamReader
from asyncio import StreamWriter, AbstractEventLoop, StreamReader
from asyncio.trsock import TransportSocket
from core import utils from core import utils
from .tcp_server import TCPServer from .tcp_server import TCPServer
from .udp_server import UDPServer from .udp_server import UDPServer
class Client: class Client:
def __init__(self, reader: StreamReader, writer: StreamWriter): def __init__(self, reader: StreamReader, writer: StreamWriter, core: Core) -> "Client":
self.reader = reader self.reader = reader
self.writer = writer self.writer = writer
self.log = utils.get_logger("client(id: )") self.log = utils.get_logger("client(id: )")
self.addr = writer.get_extra_info("sockname") self.addr = writer.get_extra_info("sockname")
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
self.Core = core
self.cid = 0 self.cid = 0
self.key: str = None self.key: str = None
self.nick: str = None self.nick: str = None
@ -32,6 +33,7 @@ class Client:
async def sync_resources(self) -> None: ... async def sync_resources(self) -> None: ...
async def recv(self) -> bytes: ... async def recv(self) -> bytes: ...
async def last_handle(self) -> bytes: ... async def last_handle(self) -> bytes: ...
def _update_logger(self) -> None: ...
class Core: class Core:

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.tcp_server.py # File core.tcp_server.py
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 0.1.2 # Version 0.1.6
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import asyncio import asyncio

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.tcp_server.pyi # File core.tcp_server.pyi
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 0.1.2 # Version 0.1.6
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import asyncio import asyncio

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev # Developed by KuiToi Dev
# File core.utils.py # File core.utils.py
# Written by: SantaSpeen # Written by: SantaSpeen
# Version 0.1.0 # Version 0.1.6
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023

View File

@ -12,7 +12,7 @@ import argparse
parser = argparse.ArgumentParser(description='KuiToi-Server - BeamingDrive server compatible with BeamMP clients!') parser = argparse.ArgumentParser(description='KuiToi-Server - BeamingDrive server compatible with BeamMP clients!')
parser.add_argument('-v', '--version', action="store_true", help='Print version and exit.', default=False) parser.add_argument('-v', '--version', action="store_true", help='Print version and exit.', default=False)
parser.add_argument('--config', help='Patch to config file.', nargs='?', default=None, type=str) parser.add_argument('--config', help='Patch to config file.', nargs='?', default=None, type=str)
parser.add_argument('--language', help='Setting localisation.', nargs='?', default="en", type=str) parser.add_argument('--language', help='Setting localisation.', nargs='?', default=None, type=str)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -7,17 +7,19 @@
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import os import os
import secrets
import yaml import yaml
class Config: class Config:
def __init__(self, auth=None, game=None, server=None): def __init__(self, auth=None, game=None, server=None, web=None):
self.Auth = auth or {"key": None, "private": True} self.Auth = auth or {"key": None, "private": True}
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1} self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
self.Server = server or {"name": "KuiToi-Server", self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!", "language": "en",
"description": "This server uses KuiToi!", "server_ip": "0.0.0.0", "server_port": 30814, "debug": False}
"server_port": 30814, "server_ip": "0.0.0.0", "debug": False} # self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
# "secret_key": secrets.token_hex(16)}
def __repr__(self): def __repr__(self):
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server) return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
@ -25,16 +27,16 @@ class Config:
class ConfigProvider: class ConfigProvider:
def __init__(self, config_patch): def __init__(self, config_path):
self.config_patch = config_patch self.config_path = config_path
self.config = Config() self.config = Config()
def open_config(self): def open_config(self):
if not os.path.exists(self.config_patch): if not os.path.exists(self.config_path):
with open(self.config_patch, "w", encoding="utf-8") as f: with open(self.config_path, "w", encoding="utf-8") as f:
yaml.dump(self.config, f) yaml.dump(self.config, f)
try: try:
with open(self.config_patch, "r", encoding="utf-8") as f: with open(self.config_path, "r", encoding="utf-8") as f:
self.config = yaml.load(f.read(), yaml.Loader) self.config = yaml.load(f.read(), yaml.Loader)
except yaml.YAMLError: except yaml.YAMLError:
print("You have errors in the YAML syntax.") print("You have errors in the YAML syntax.")
@ -44,5 +46,5 @@ class ConfigProvider:
return self.config return self.config
def save_config(self): def save_config(self):
with open(self.config_patch, "w", encoding="utf-8") as f: with open(self.config_path, "w", encoding="utf-8") as f:
yaml.dump(self.config, f) yaml.dump(self.config, f)

View File

@ -6,7 +6,6 @@
# Version 1.1 # Version 1.1
# Licence: FPA # Licence: FPA
# (c) kuitoi.su 2023 # (c) kuitoi.su 2023
import asyncio
import builtins import builtins
import logging import logging
from typing import AnyStr from typing import AnyStr
@ -37,14 +36,9 @@ class Console:
self.__man = dict() self.__man = dict()
self.__desc = dict() self.__desc = dict()
self.__print_logger = get_logger("print") self.__print_logger = get_logger("print")
self.add_command("man", self.__create_man_message, "man - display the manual page.\n" self.add_command("man", self.__create_man_message, i18n.man_message_man, i18n.help_message_man,
"Usage: man COMMAND", "Display the manual page",
custom_completer={"man": {}}) custom_completer={"man": {}})
self.add_command("help", self.__create_help_message, self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help,
"help - display names and brief descriptions of available commands.\n"
"Usage: help [--raw]\n"
"The `help` command displays a list of all available commands along with a brief description "
"of each command.", "Display names and brief descriptions of available commands.",
custom_completer={"help": {"--raw": None}}) custom_completer={"help": {"--raw": None}})
self.completer = NestedCompleter.from_nested_dict(self.__alias) self.completer = NestedCompleter.from_nested_dict(self.__alias)
@ -68,16 +62,19 @@ class Console:
return i return i
def __create_man_message(self, argv: list) -> AnyStr: def __create_man_message(self, argv: list) -> AnyStr:
if len(argv) == 0:
return self.__man.get("man")
x = argv[0] x = argv[0]
if x in ['']: if self.__alias.get(x) is None:
return "man COMMAND" return i18n.man_command_not_found.format(x)
man_message = self.__man.get(x) man_message = self.__man.get(x)
if man_message is None:
return "man: Manual message not found."
if man_message: if man_message:
return man_message return man_message
return f'man: command "{x}" not found' else:
return i18n.man_message_not_found
# noinspection PyStringFormat
def __create_help_message(self, argv: list) -> AnyStr: def __create_help_message(self, argv: list) -> AnyStr:
self.__debug("creating help message") self.__debug("creating help message")
raw = False raw = False
@ -95,7 +92,7 @@ class Console:
if raw: if raw:
message += f"%-{max_len}s; %-{max_len_v}s; %s\n" % ("Key", "Function", "Description") message += f"%-{max_len}s; %-{max_len_v}s; %s\n" % ("Key", "Function", "Description")
else: else:
message += f" %-{max_len}s : %s\n" % ("Command", "Help message") message += f" %-{max_len}s : %s\n" % (i18n.help_command, i18n.help_message)
for k, v in self.__func.items(): for k, v in self.__func.items():
doc = self.__desc.get(k) doc = self.__desc.get(k)
@ -105,7 +102,7 @@ class Console:
else: else:
if doc is None: if doc is None:
doc = "No help message found" doc = i18n.help_message_not_found
message += f" %-{max_len}s : %s\n" % (k, doc) message += f" %-{max_len}s : %s\n" % (k, doc)
return message return message
@ -122,7 +119,7 @@ class Console:
self.__alias.update(custom_completer or {key: None}) self.__alias.update(custom_completer or {key: None})
self.__alias["man"].update({key: None}) self.__alias["man"].update({key: None})
self.__func.update({key: {"f": func}}) self.__func.update({key: {"f": func}})
self.__man.update({key: f'html<seagreen>Manual for command <b>{key}</b>\n{man}</seagreen>' if man else None}) self.__man.update({key: f'html<seagreen>{i18n.man_for} <b>{key}</b>\n{man}</seagreen>' if man else None})
self.__desc.update({key: desc}) self.__desc.update({key: desc})
self.__update_completer() self.__update_completer()
return self.__alias.copy() return self.__alias.copy()

View File

@ -4,7 +4,7 @@ import types
from core import get_logger from core import get_logger
class BEAMP: class KuiToi:
def __init__(self, name=None): def __init__(self, name=None):
if name is None: if name is None:
@ -24,7 +24,6 @@ class BEAMP:
ev.call_event(event_name, *data) ev.call_event(event_name, *data)
class PluginsLoader: class PluginsLoader:
def __init__(self, plugins_dir): def __init__(self, plugins_dir):
@ -40,7 +39,7 @@ class PluginsLoader:
try: try:
self.log.debug(f"Loading plugin: {file}") self.log.debug(f"Loading plugin: {file}")
plugin = types.ModuleType('plugin') plugin = types.ModuleType('plugin')
plugin.BEAMP = BEAMP plugin.KuiToi = KuiToi
plugin.print = print plugin.print = print
file = os.path.join(self.__plugins_dir, file) file = os.path.join(self.__plugins_dir, file)
with open(f'{file}', 'r') as f: with open(f'{file}', 'r') as f:

View File

@ -1,8 +1,45 @@
{ {
"": "Basic phases",
"hello": "Hello from KuiToi-Server!", "hello": "Hello from KuiToi-Server!",
"config_file": "Use %s for config.", "config_path": "Use {} for config.",
"debug": "Getting new logging with DEBUG level!", "init_ok": "Initializing ready.",
"config_info": "Server config: %s", "start": "Server started!",
"init": "Initializing ready.", "stop": "Goodbye!",
"ready": "Server started!"
"": "Server auth",
"auth_need_key": "BEAM key needed for starting the server!",
"auth_empty_key": "Key is empty!",
"auth_cannot_open_browser": "Cannot open browser: {}",
"auth_use_link": "Use this link: {}",
"": "GUI phases",
"GUI_yes": "Yes",
"GUI_no": "No",
"GUI_ok": "Ok",
"GUI_cancel": "Cancel",
"GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?",
"GUI_enter_key_message": "Please type your key:",
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
"": "Command: man",
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
"help_message_man": "Display the manual page for COMMAND.",
"man_for": "Manual for command",
"man_message_not_found": "man: Manual message not found.",
"man_command_not_found": "man: command \"{}\" not found!",
"": "Command: help",
"man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.",
"help_message_help": "Display names and brief descriptions of available commands",
"help_command": "Command",
"help_message": "Help message",
"help_message_not_found": "No help message found",
"": "Command: stop",
"man_message_stop": "stop - Just shutting down the server.\nUsage: stop",
"help_message_stop": "Server shutdown.",
"": "Command: exit",
"man_message_exit": "exit - Just shutting down the server.\nUsage: stop",
"help_message_exit": "Server shutdown."
} }

View File

@ -1,8 +1,45 @@
{ {
"": "Basic phases",
"hello": "Привет из KuiToi-Server!", "hello": "Привет из KuiToi-Server!",
"config_file": "Используй %s для настройки.", "config_path": "Используя {} для настройки.",
"debug": "Начата новая сессия логирования с уровнем DEBUG!", "init_ok": "Инициализация окончена.",
"config_info": "Конфиг сервера: %s", "start": "Сервер запущен!",
"init": "Инициализация окончена.", "stop": "Сервер остановлен!",
"ready": "Сервер запущен!"
"": "Server auth",
"auth_need_key": "Нужен BEAMP ключ для запуска!",
"auth_empty_key": "BEAMP ключ пустой!",
"auth_cannot_open_browser": "Не получилось открыть браузер: {}",
"auth_use_link": "Используй эту ссылку: {}",
"": "GUI phases",
"GUI_yes": "Да",
"GUI_no": "Нет",
"GUI_ok": "Окей",
"GUI_cancel": "Отмена",
"GUI_need_key_message": "Нужен BEAMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
"GUI_enter_key_message": "Пожалуйста введите ключ:",
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
"": "Command: man",
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
"help_message_man": "Показывает страничку помощи для COMMAND.",
"man_for": "Страничка помощи для",
"man_message_not_found": "man: Страничка помощи не найдена.",
"man_command_not_found": "man: Команда \"{}\" не найдена!",
"": "Command: help",
"man_message_help": "help - Показывает названия и краткое описание команд.\nИспользование: help [--raw]\nКоманда `help` выводит список всех доступных команд, и краткое описание для каждой команды.",
"help_message_help": "Показывает названия и краткое описание команд",
"help_command": "Команда",
"help_message": "Текст",
"help_message_not_found": "Нет текста",
"": "Command: stop",
"man_message_stop": "stop - Выключает сервер.\nИспользование: stop",
"help_message_stop": "Выключает сервер.",
"": "Command: exit",
"man_message_exit": "exit - Выключает сервер.\nИспользование: exit",
"help_message_exit": "Выключает сервер."
} }

View File

@ -1,10 +1,90 @@
class i18n: class i18n:
hello: str = "Hello from KuiToi-Server!" # Basic phases
config_file: str = "Use kuitoi.yml for config." hello: str = data["hello"]
debug: str = "Getting new logging with DEBUG level!" config_path: str = data["config_path"]
config_info: str = "Server config: %s" init_ok: str = data["init_ok"]
init: str = "Initializing ready." start: str = data["start"]
ready: str = "Server started!" stop: str = data["stop"]
i18n_data = {"hello":"Hello from KuiToi-Server!","config_file":"Use kuitoi.yml for config.",
"debug":"Getting new logging with DEBUG level!","config_info":"Server config: %s", # Server auth
"init":"Initializing ready.","ready":"Server started!"} auth_need_key: str = data["auth_need_key"]
auth_empty_key: str = data["auth_empty_key"]
auth_cannot_open_browser: str = data["auth_cannot_open_browser"]
auth_use_link: str = data["auth_use_link"]
# GUI phases
GUI_yes: str = data["GUI_yes"]
GUI_no: str = data["GUI_no"]
GUI_ok: str = data["GUI_ok"]
GUI_cancel: str = data["GUI_cancel"]
GUI_need_key_message: str = data["GUI_need_key_message"]
GUI_enter_key_message: str = data["GUI_enter_key_message"]
GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
# Command: man
man_message_man: str = data["man_message_man"]
help_message_man: str = data["help_message_man"]
man_for: str = data["man_for"]
man_message_not_found: str = data["man_message_not_found"]
man_command_not_found: str = data["man_command_not_found"]
# Command: help
man_message_help: str = data["man_message_help"]
help_message_help: str = data["help_message_help"]
help_command: str = data["help_command"]
help_message: str = data["help_message"]
help_message_not_found: str = data["help_message_not_found"]
# Command: stop
man_message_stop: str = data["man_message_stop"]
help_message_stop: str = data["help_message_stop"]
# Command: exit
man_message_exit: str = data["man_message_exit"]
help_message_exit: str = data["help_message_exit"]
data = {
"": "Basic phases",
"hello": "Hello from KuiToi-Server!",
"config_path": "Use {} for config.",
"init_ok": "Initializing ready.",
"start": "Server started!",
"stop": "Goodbye!",
"": "Server auth",
"auth_need_key": "BEAM key needed for starting the server!",
"auth_empty_key": "Key is empty!",
"auth_cannot_open_browser": "Cannot open browser: {}",
"auth_use_link": "Use this link: {}",
"": "GUI phases",
"GUI_yes": "Yes",
"GUI_no": "No",
"GUI_ok": "Ok",
"GUI_cancel": "Cancel",
"GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?",
"GUI_enter_key_message": "Please type your key:",
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
"": "Command: man",
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
"help_message_man": "Display the manual page for COMMAND.",
"man_for": "Manual for command",
"man_message_not_found": "man: Manual message not found.",
"man_command_not_found": "man: command \"{}\" not found!",
"": "Command: help",
"man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.",
"help_message_help": "Display names and brief descriptions of available commands",
"help_command": "Command",
"help_message": "Help message",
"help_message_not_found": "No help message found",
"": "Command: stop",
"man_message_stop": "stop - Just shutting down the server.\nUsage: stop",
"help_message_stop": "Server shutdown.",
"": "Command: exit",
"man_message_exit": "exit - Just shutting down the server.\nUsage: stop",
"help_message_exit": "Server shutdown."
}

View File

@ -16,12 +16,51 @@ from core.utils import get_logger
class i18n: class i18n:
def __init__(self, data): def __init__(self, data):
# Basic phases
self.hello: str = data["hello"] self.hello: str = data["hello"]
self.config_file: str = data["config_file"] self.config_path: str = data["config_path"]
self.debug: str = data["debug"] self.init_ok: str = data["init_ok"]
self.config_info: str = data["config_info"] self.start: str = data["start"]
self.init: str = data["init"] self.stop: str = data["stop"]
self.ready: str = data["ready"]
# Server auth
self.auth_need_key: str = data["auth_need_key"]
self.auth_empty_key: str = data["auth_empty_key"]
self.auth_cannot_open_browser: str = data["auth_cannot_open_browser"]
self.auth_use_link: str = data["auth_use_link"]
# GUI phases
self.GUI_yes: str = data["GUI_yes"]
self.GUI_no: str = data["GUI_no"]
self.GUI_ok: str = data["GUI_ok"]
self.GUI_cancel: str = data["GUI_cancel"]
self.GUI_need_key_message: str = data["GUI_need_key_message"]
self.GUI_enter_key_message: str = data["GUI_enter_key_message"]
self.GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"]
# Command: man
self.man_message_man: str = data["man_message_man"]
self.help_message_man: str = data["help_message_man"]
self.man_for: str = data["man_for"]
self.man_message_not_found: str = data["man_message_not_found"]
self.man_command_not_found: str = data["man_command_not_found"]
# Command: help
self.man_message_help: str = data["man_message_help"]
self.help_message_help: str = data["help_message_help"]
self.help_command: str = data["help_command"]
self.help_message: str = data["help_message"]
self.help_message_not_found: str = data["help_message_not_found"]
# Command: help
self.man_message_stop: str = data["man_message_stop"]
self.help_message_stop: str = data["help_message_stop"]
# Command: exit
self.man_message_exit: str = data["man_message_exit"]
self.help_message_exit: str = data["help_message_exit"]
self.data = data
class MultiLanguage: class MultiLanguage:
@ -39,19 +78,57 @@ class MultiLanguage:
def set_language(self, language): def set_language(self, language):
if language is None: if language is None:
return language = "en"
self.log.debug(f"set_language({language})") self.log.debug(f"set_language({language})")
self.language = language self.language = language
if language != "en": if language != "en":
self.open_file() self.open_file()
else: else:
# noinspection PyDictDuplicateKeys
self.__data = { self.__data = {
"": "Basic phases",
"hello": "Hello from KuiToi-Server!", "hello": "Hello from KuiToi-Server!",
"config_file": "Use %s for config.", "config_path": "Use {} for config.",
"debug": "Getting new logging with DEBUG level!", "init_ok": "Initializing ready.",
"config_info": "Server config: %s", "start": "Server started!",
"init": "Initializing ready.", "stop": "Goodbye!",
"ready": "Server started!"
"": "Server auth",
"auth_need_key": "BEAM key needed for starting the server!",
"auth_empty_key": "Key is empty!",
"auth_cannot_open_browser": "Cannot open browser: {}",
"auth_use_link": "Use this link: {}",
"": "GUI phases",
"GUI_yes": "Yes",
"GUI_no": "No",
"GUI_ok": "Ok",
"GUI_cancel": "Cancel",
"GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?",
"GUI_enter_key_message": "Please type your key:",
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
"": "Command: man",
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
"help_message_man": "Display the manual page for COMMAND.",
"man_for": "Manual for command",
"man_message_not_found": "man: Manual message not found.",
"man_command_not_found": "man: command \"{}\" not found!",
"": "Command: help",
"man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.",
"help_message_help": "Display names and brief descriptions of available commands",
"help_command": "Command",
"help_message": "Help message",
"help_message_not_found": "No help message found",
"": "Command: stop",
"man_message_stop": "stop - Just shutting down the server.\nUsage: stop",
"help_message_stop": "Server shutdown.",
"": "Command: exit",
"man_message_exit": "exit - Just shutting down the server.\nUsage: stop",
"help_message_exit": "Server shutdown."
} }
self.__i18n = i18n(self.__data) self.__i18n = i18n(self.__data)
@ -63,7 +140,8 @@ class MultiLanguage:
self.__data.update(json.load(f)) self.__data.update(json.load(f))
return return
except JSONDecodeError: except JSONDecodeError:
self.log.error(f"Localisation \"{self.language}.json\" have JsonDecodeError. Using default localisation: en.") self.log.error(
f"Localisation \"{self.language}.json\" have JsonDecodeError. Using default localisation: en.")
except FileNotFoundError: except FileNotFoundError:
self.log.warning(f"Localisation \"{self.language}.json\" not found; Using default localisation: en.") self.log.warning(f"Localisation \"{self.language}.json\" not found; Using default localisation: en.")
self.set_language("en") self.set_language("en")