mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 08:15:42 +00:00
Compare commits
16 Commits
3f688df30b
...
6e46af4c13
Author | SHA1 | Date | |
---|---|---|---|
6e46af4c13 | |||
d21798aaf1 | |||
22105b2030 | |||
19c121f208 | |||
85c379bd9e | |||
a15eb316bb | |||
cecd6f13d6 | |||
df171aaa70 | |||
5a1cb8a133 | |||
d44cff1116 | |||
bc6cf60099 | |||
fc886ef415 | |||
bd7b988b01 | |||
4f5a6edc48 | |||
541849642c | |||
f364f29d79 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -137,3 +137,4 @@ dmypy.json
|
||||
/src/plugins
|
||||
/test/
|
||||
*test.py
|
||||
logs/
|
27
README.md
27
README.md
@ -7,43 +7,48 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
||||
## TODOs
|
||||
|
||||
- [ ] Server core
|
||||
- [x] BEAMP System
|
||||
- [x] Private access without key (Direct connect)
|
||||
- [x] Server authentication (For public access)
|
||||
- [x] BeamMP System
|
||||
- [x] Private access (Without key, Direct connect)
|
||||
- [x] Public access (With key, listing in Launcher)
|
||||
- [X] Player authentication
|
||||
- [ ] TCP Server part:
|
||||
- [x] Handle code
|
||||
- [x] Understanding beamp header
|
||||
- [x] Understanding BeamMP header
|
||||
- [ ] Upload mods
|
||||
- [x] Connecting to the world
|
||||
- [x] Chat
|
||||
- [ ] Player counter _(Code: Ss)_
|
||||
- [ ] Car state synchronizations _(Codes: We, Vi)_
|
||||
- [ ] "ABG:" (compressed data)
|
||||
- [x] Decompress data
|
||||
- [ ] Vehicle data
|
||||
- [ ] Players synchronizations
|
||||
- [ ] UDP Server part:
|
||||
- [ ] Players synchronizations
|
||||
- [ ] Ping
|
||||
- [ ] Player counter
|
||||
- [ ] Players synchronizations _(Code: Zp)_
|
||||
- [ ] Ping _(Code: p)_
|
||||
- [x] Additional:
|
||||
- [x] Logger
|
||||
- [x] Just logging
|
||||
- [x] Log in file
|
||||
- [x] Log history (.1.log, .2.log, ...)
|
||||
- [x] Console:
|
||||
- [x] Tabulation
|
||||
- [ ] _(Deferred)_ Static text
|
||||
- [ ] _(Deferred)_ Static text (bug)
|
||||
- [x] Events System
|
||||
- [x] Call events
|
||||
- [x] Create custom events
|
||||
- [ ] Return from events
|
||||
- [x] Plugins support
|
||||
- [x] Load Python plugins
|
||||
- [ ] Load Lua plugins (Original BEAMP compatibility)
|
||||
- [ ] Load Lua plugins (Original BeamMP compatibility)
|
||||
- [x] MultiLanguage (i18n support)
|
||||
- [x] Core
|
||||
- [x] Console
|
||||
- [x] WebAPI
|
||||
- [x] HTTP API Server (fastapi)
|
||||
- [x] Stop and Start with core
|
||||
- [x] Custom logger
|
||||
- [x] Configure FastAPI logger
|
||||
- [ ] Sync with event system
|
||||
- [ ] Add methods...
|
||||
- [ ] [Documentation](docs/en/readme.md)
|
||||
|
||||
## Installation
|
||||
|
@ -7,8 +7,8 @@
|
||||
"stop": "Сервер остановлен!",
|
||||
|
||||
"": "Server auth",
|
||||
"auth_need_key": "Нужен BEAMP ключ для запуска!",
|
||||
"auth_empty_key": "BEAMP ключ пустой!",
|
||||
"auth_need_key": "Нужен BeamMP ключ для запуска!",
|
||||
"auth_empty_key": "BeamMP ключ пустой!",
|
||||
"auth_cannot_open_browser": "Не получилось открыть браузер: {}",
|
||||
"auth_use_link": "Используй эту ссылку: {}",
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
"GUI_no": "Нет",
|
||||
"GUI_ok": "Окей",
|
||||
"GUI_cancel": "Отмена",
|
||||
"GUI_need_key_message": "Нужен BEAMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
|
||||
"GUI_need_key_message": "Нужен BeamMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
|
||||
"GUI_enter_key_message": "Пожалуйста введите ключ:",
|
||||
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
|
||||
|
||||
|
@ -43,7 +43,7 @@ Server:
|
||||
server_ip: 0.0.0.0
|
||||
server_port: 30814
|
||||
```
|
||||
* Если поставить `private: false` и не установить `key`, то сервер запросит BEAMP ключ, без него не запустится.
|
||||
* Введя BEAMP ключ сервер появится в списке лаунчера.
|
||||
* Если поставить `private: false` и не установить `key`, то сервер запросит BeamMP ключ, без него не запустится.
|
||||
* Введя BeamMP ключ сервер появится в списке лаунчера.
|
||||
* Взять ключ можно тут: [https://beammp.com/k/keys](https://beammp.com/k/keys)
|
||||
|
||||
|
@ -67,13 +67,13 @@ ev.builtins_hook()
|
||||
log.info(i18n.hello)
|
||||
log.info(i18n.config_path.format(config_path))
|
||||
|
||||
log.debug("Initializing BEAMP Server system...")
|
||||
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"
|
||||
if shortcuts.yes_no_dialog(
|
||||
title='BEAMP Server Key',
|
||||
title='BeamMP Server Key',
|
||||
text=i18n.GUI_need_key_message,
|
||||
yes_text=i18n.GUI_yes,
|
||||
no_text=i18n.GUI_no).run():
|
||||
@ -84,12 +84,12 @@ if not config.Auth['private'] and not config.Auth['key']:
|
||||
log.error(i18n.auth_cannot_open_browser.format(e))
|
||||
log.info(i18n.auth_use_link.format(url))
|
||||
shortcuts.message_dialog(
|
||||
title='BEAMP Server Key',
|
||||
title='BeamMP 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='BeamMP Server Key',
|
||||
text=i18n.GUI_enter_key_message,
|
||||
ok_text=i18n.GUI_ok,
|
||||
cancel_text=i18n.GUI_cancel).run()
|
||||
|
@ -23,6 +23,7 @@ class Client:
|
||||
def __init__(self, reader, writer, core):
|
||||
self.reader = reader
|
||||
self.writer = writer
|
||||
self.down_rw = (None, None)
|
||||
self.log = utils.get_logger("client(None:0)")
|
||||
self.addr = writer.get_extra_info("sockname")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
@ -61,7 +62,7 @@ class Client:
|
||||
async def tcp_send(self, data):
|
||||
|
||||
# TNetwork.cpp; Line: 383
|
||||
# BEAMP TCP protocol sends a header of 4 bytes, followed by the data.
|
||||
# BeamMP TCP protocol sends a header of 4 bytes, followed by the data.
|
||||
# [][][][][][]...[]
|
||||
# ^------^^---...-^
|
||||
# size data
|
||||
@ -108,18 +109,31 @@ class Client:
|
||||
return data
|
||||
|
||||
async def sync_resources(self):
|
||||
await self.tcp_send(b"P" + bytes(f"{self.cid}", "utf-8"))
|
||||
while True:
|
||||
data = await self.recv()
|
||||
if data.startswith(b"SR"):
|
||||
await self.tcp_send(b"-") # Cannot handle mods for now.
|
||||
if data.startswith(b"f"):
|
||||
# TODO: SendFile
|
||||
pass
|
||||
elif data.startswith(b"SR"):
|
||||
# TODO: Create mods list
|
||||
self.log.debug("Sending Mod Info")
|
||||
mods = []
|
||||
mod_list = b''
|
||||
# * code *
|
||||
if len(mods) == 0:
|
||||
await self.tcp_send(b"-")
|
||||
else:
|
||||
await self.tcp_send(mod_list)
|
||||
data = await self.recv()
|
||||
if data == b"Done":
|
||||
await self.tcp_send(b"M/levels/" + bytes(config.Game['map'], 'utf-8') + b"/info.json")
|
||||
await self.last_handle()
|
||||
break
|
||||
|
||||
async def last_handle(self):
|
||||
async def looper(self):
|
||||
# self.is_disconnected()
|
||||
self.log.debug(f"Alive: {self.alive}")
|
||||
await self.tcp_send(b"P" + bytes(f"{self.cid}", "utf-8"))
|
||||
await self.sync_resources()
|
||||
while self.alive:
|
||||
data = await self.recv()
|
||||
if data == b"":
|
||||
@ -147,8 +161,9 @@ class Core:
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.run = False
|
||||
self.direct = False
|
||||
self.clients = {}
|
||||
self.clients_counter = 0
|
||||
self.clients = []
|
||||
self.clients_by_id = {}
|
||||
self.clients_by_nick = {}
|
||||
self.mods_dir = "./mods"
|
||||
self.mods_list = [0, ]
|
||||
self.server_ip = config.Server["server_ip"]
|
||||
@ -160,33 +175,42 @@ class Core:
|
||||
self.web_stop = None
|
||||
|
||||
self.client_major_version = "2.0"
|
||||
self.BEAMP_version = "3.2.0"
|
||||
self.BeamMP_version = "3.2.0"
|
||||
|
||||
def get_client(self, sock=None, cid=None):
|
||||
def get_client(self, sock=None, cid=None, nick=None):
|
||||
if cid:
|
||||
return self.clients.get(cid)
|
||||
return self.clients_by_id.get(cid)
|
||||
if nick:
|
||||
return self.clients_by_nick.get(nick)
|
||||
if sock:
|
||||
return self.clients.get(sock.getsockname())
|
||||
return self.clients_by_nick.get(sock.getsockname())
|
||||
|
||||
def insert_client(self, client):
|
||||
self.log.debug(f"Inserting client: {client.cid}")
|
||||
self.clients.update({client.cid: client, client.nick: client})
|
||||
self.clients_by_nick.update({client.nick: client})
|
||||
self.clients_by_id.update({client.cid: client})
|
||||
self.clients[client.cid] = client
|
||||
|
||||
def create_client(self, *args, **kwargs):
|
||||
client = Client(*args, **kwargs)
|
||||
self.clients_counter += 1
|
||||
client.id = self.clients_counter
|
||||
cid = 1
|
||||
for client in self.clients:
|
||||
if client.cid == cid:
|
||||
cid += 1
|
||||
else:
|
||||
break
|
||||
client.cid = cid
|
||||
client._update_logger()
|
||||
self.log.debug(f"Create client: {client.cid}; clients_counter: {self.clients_counter}")
|
||||
self.log.debug(f"Create client; client.cid: {client.cid};")
|
||||
return client
|
||||
|
||||
async def check_alive(self):
|
||||
await asyncio.sleep(5)
|
||||
self.log.debug(f"Checking if clients is alive")
|
||||
for cl in self.clients.values():
|
||||
d = await cl.is_disconnected()
|
||||
for client in self.clients:
|
||||
d = client.is_disconnected()
|
||||
if d:
|
||||
self.log.debug(f"Client ID: {cl.id} died...")
|
||||
self.log.debug(f"Client ID: {client.cid} died...")
|
||||
|
||||
@staticmethod
|
||||
def start_web():
|
||||
@ -224,7 +248,7 @@ class Core:
|
||||
while self.run:
|
||||
data = {"uuid": config.Auth["key"], "players": len(self.clients), "maxplayers": config.Game["players"],
|
||||
"port": config.Server["server_port"], "map": f"/levels/{config.Game['map']}/info.json",
|
||||
"private": config.Auth['private'], "version": self.BEAMP_version,
|
||||
"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}
|
||||
@ -337,5 +361,6 @@ class Core:
|
||||
def stop(self):
|
||||
self.run = False
|
||||
self.log.info(i18n.stop)
|
||||
if config.WebAPI["enabled"]:
|
||||
asyncio.run(self.web_stop())
|
||||
exit(0)
|
||||
|
@ -7,7 +7,7 @@
|
||||
import asyncio
|
||||
from asyncio import StreamWriter, StreamReader
|
||||
from threading import Thread
|
||||
from typing import Callable
|
||||
from typing import Callable, List, Dict, Tuple
|
||||
|
||||
from core import utils
|
||||
from .tcp_server import TCPServer
|
||||
@ -19,6 +19,7 @@ class Client:
|
||||
def __init__(self, reader: StreamReader, writer: StreamWriter, core: Core) -> "Client":
|
||||
self.reader = reader
|
||||
self.writer = writer
|
||||
self.down_rw: Tuple[StreamReader, StreamWriter] | Tuple[None, None] = (None, None)
|
||||
self.log = utils.get_logger("client(id: )")
|
||||
self.addr = writer.get_extra_info("sockname")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
@ -34,7 +35,7 @@ class Client:
|
||||
async def tcp_send(self, data: bytes) -> None: ...
|
||||
async def sync_resources(self) -> None: ...
|
||||
async def recv(self) -> bytes: ...
|
||||
async def last_handle(self) -> bytes: ...
|
||||
async def looper(self) -> None: ...
|
||||
def _update_logger(self) -> None: ...
|
||||
|
||||
|
||||
@ -44,7 +45,9 @@ class Core:
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.run = False
|
||||
self.direct = False
|
||||
self.clients = dict()
|
||||
self.clients: List[Client]= []
|
||||
self.clients_by_id: Dict[{int: Client}]= {}
|
||||
self.clients_by_nick: Dict[{str: Client}] = {}
|
||||
self.clients_counter: int = 0
|
||||
self.mods_dir: str = "mods"
|
||||
self.mods_list: list = []
|
||||
@ -55,7 +58,7 @@ class Core:
|
||||
self.web_thread: Thread = None
|
||||
self.web_stop: Callable = lambda: None
|
||||
self.client_major_version = "2.0"
|
||||
self.BEAMP_version = "3.2.0"
|
||||
self.BeamMP_version = "3.2.0"
|
||||
def insert_client(self, client: Client) -> None: ...
|
||||
def create_client(self, *args, **kwargs) -> Client: ...
|
||||
async def check_alive(self) -> None: ...
|
||||
|
@ -25,10 +25,7 @@ class TCPServer:
|
||||
self.log.info(f"Identifying new ClientConnection...")
|
||||
data = await client.recv()
|
||||
self.log.debug(f"recv1 data: {data}")
|
||||
if len(data) > 50:
|
||||
await client.kick("Too long data")
|
||||
return False, None
|
||||
if "VC2.0" not in data.decode("utf-8"):
|
||||
if data.decode("utf-8") != f"VC{self.Core.client_major_version}":
|
||||
await client.kick("Outdated Version.")
|
||||
return False, None
|
||||
else:
|
||||
@ -57,8 +54,9 @@ class TCPServer:
|
||||
self.log.error(f"Auth error: {e}")
|
||||
await client.kick('Invalid authentication data! Try to connect in 5 minutes.')
|
||||
|
||||
# TODO: Password party
|
||||
# await client.tcp_send(b"S") # Ask client key (How?)
|
||||
for _client in self.Core.clients:
|
||||
if _client.nick == client.nick and _client.guest == client.guest:
|
||||
await client.kick('Stale Client (replaced by new client)')
|
||||
|
||||
ev.call_event("on_auth", client)
|
||||
|
||||
@ -70,9 +68,17 @@ class TCPServer:
|
||||
|
||||
return True, client
|
||||
|
||||
async def handle_download(self, writer):
|
||||
# TODO: HandleDownload
|
||||
self.log.debug(f"Client: \"IP: {0!r}; ID: {0}\" - HandleDownload!")
|
||||
async def set_down_rw(self, reader, writer):
|
||||
try:
|
||||
cid = (await reader.read(1)).decode() # FIXME: wtf? 1 byte?
|
||||
self.log.debug(f"Client: \"ID: {cid}\" - HandleDownload!")
|
||||
if not cid.isdigit():
|
||||
return False
|
||||
for _client in self.Core.clients:
|
||||
if _client.cid == cid:
|
||||
_client.down_rw = (reader, writer)
|
||||
return True
|
||||
finally:
|
||||
return False
|
||||
|
||||
async def handle_code(self, code, reader, writer):
|
||||
@ -80,12 +86,11 @@ class TCPServer:
|
||||
case "C":
|
||||
result, client = await self.auth_client(reader, writer)
|
||||
if result:
|
||||
await client.sync_resources()
|
||||
# await client.kick("Authentication success! Server not ready.")
|
||||
await client.looper()
|
||||
return True
|
||||
return False
|
||||
case "D":
|
||||
return await self.handle_download(writer)
|
||||
return await self.set_down_rw(reader, writer)
|
||||
case "P":
|
||||
writer.write(b"P")
|
||||
await writer.drain()
|
||||
@ -115,7 +120,7 @@ class TCPServer:
|
||||
self.log.debug("Starting TCP server.")
|
||||
try:
|
||||
server = await asyncio.start_server(self.handle_client, self.host, self.port,
|
||||
backlog=config.Game["players"] + 1)
|
||||
backlog=int(config.Game["players"] * 1.3))
|
||||
except OSError as e:
|
||||
self.log.error(f"Error: {e}")
|
||||
self.Core.run = False
|
||||
|
@ -20,7 +20,7 @@ class TCPServer:
|
||||
self.port = port
|
||||
self.loop = asyncio.get_event_loop()
|
||||
async def auth_client(self, reader: StreamReader, writer: StreamWriter) -> Tuple[bool, Client]: ...
|
||||
async def handle_download(self, writer: StreamWriter) -> bool: ...
|
||||
async def set_down_rw(self, reader: StreamReader, writer: StreamWriter) -> bool: ...
|
||||
async def handle_code(self, code: str, reader: StreamReader, writer: StreamWriter) -> bool: ...
|
||||
async def handle_client(self, reader: StreamReader, writer: StreamWriter) -> None: ...
|
||||
async def start(self) -> None: ...
|
||||
|
@ -1,21 +1,37 @@
|
||||
# Developed by KuiToi Dev
|
||||
# File core.utils.py
|
||||
# Written by: SantaSpeen
|
||||
# Version 1.0
|
||||
# Version 1.1
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import tarfile
|
||||
|
||||
log_format = "[%(asctime)s | %(name)-14s | %(levelname)-5s] %(message)s"
|
||||
log_format_access = '[%(asctime)s | %(name)-14s | %(levelname)-5s] %(client_addr)s - "%(request_line)s" %(status_code)s'
|
||||
log_file = "server.log"
|
||||
log_dir = "./logs/"
|
||||
log_file = log_dir + "server.log"
|
||||
log_level = logging.INFO
|
||||
# Инициализируем логирование
|
||||
logging.basicConfig(level=log_level, format=log_format)
|
||||
# Настройка логирование в файл.
|
||||
# if os.path.exists(log_file):
|
||||
# os.remove(log_file)
|
||||
if not os.path.exists(log_dir):
|
||||
os.mkdir(log_dir)
|
||||
if os.path.exists(log_file):
|
||||
mtime = os.path.getmtime(log_file)
|
||||
gz_path = log_dir + datetime.datetime.fromtimestamp(mtime).strftime('%d.%m.%Y') + "-%s.tar.gz"
|
||||
index = 1
|
||||
while True:
|
||||
if not os.path.exists(gz_path % index):
|
||||
break
|
||||
index += 1
|
||||
with tarfile.open(gz_path % index, "w:gz") as tar:
|
||||
logs_files = [log_file, "./logs/web.log", "./logs/web_access.log"]
|
||||
for file in logs_files:
|
||||
if os.path.exists(file):
|
||||
tar.add(file, os.path.basename(file))
|
||||
os.remove(file)
|
||||
fh = logging.FileHandler(log_file, encoding='utf-8')
|
||||
fh.setFormatter(logging.Formatter(log_format))
|
||||
|
||||
|
@ -14,6 +14,7 @@ from prompt_toolkit import PromptSession, print_formatted_text, HTML
|
||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from prompt_toolkit.history import FileHistory
|
||||
from prompt_toolkit.patch_stdout import patch_stdout
|
||||
|
||||
from core import get_logger
|
||||
|
||||
@ -186,8 +187,12 @@ class Console:
|
||||
session = PromptSession(history=FileHistory('./.cmdhistory'))
|
||||
while True:
|
||||
try:
|
||||
cmd_in = await session.prompt_async(self.__prompt_in,
|
||||
completer=self.completer, auto_suggest=AutoSuggestFromHistory())
|
||||
with patch_stdout():
|
||||
cmd_in = await session.prompt_async(
|
||||
self.__prompt_in,
|
||||
completer=self.completer,
|
||||
auto_suggest=AutoSuggestFromHistory()
|
||||
)
|
||||
cmd_s = cmd_in.split(" ")
|
||||
cmd = cmd_s[0]
|
||||
if cmd == "":
|
||||
@ -210,13 +215,3 @@ class Console:
|
||||
def stop(self, *args, **kwargs):
|
||||
self.__is_run = False
|
||||
raise KeyboardInterrupt
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
# c = Console()
|
||||
# c.logger_hook()
|
||||
# c.builtins_hook()
|
||||
# log = logging.getLogger(name="name")
|
||||
# log.info("Starting console")
|
||||
# print("Starting console")
|
||||
# asyncio.run(c.start())
|
||||
|
@ -21,7 +21,7 @@ class EventsSystem:
|
||||
self.log.debug(f"register_event({event_name}, {event_func}):")
|
||||
if not callable(event_func):
|
||||
self.log.error(f"Cannot add event '{event_name}'. "
|
||||
f"Use `BEAMP.add_event({event_name}', function)` instead. Skipping it...")
|
||||
f"Use `KuiToi.add_event({event_name}', function)` instead. Skipping it...")
|
||||
return
|
||||
if event_name not in self.__events:
|
||||
self.__events.update({str(event_name): [event_func]})
|
||||
|
@ -8,7 +8,7 @@ class KuiToi:
|
||||
|
||||
def __init__(self, name=None):
|
||||
if name is None:
|
||||
raise Exception("BEAMP: Name is required")
|
||||
raise Exception("BeamMP: Name is required")
|
||||
self.log = get_logger(f"PluginsLoader | {name}")
|
||||
self.name = name
|
||||
|
||||
@ -43,7 +43,7 @@ class PluginsLoader:
|
||||
plugin.print = print
|
||||
file = os.path.join(self.__plugins_dir, file)
|
||||
with open(f'{file}', 'r') as f:
|
||||
code = f.read().replace("import BEAMP\n", "")
|
||||
code = f.read().replace("import KuiToi\n", "")
|
||||
exec(code, plugin.__dict__)
|
||||
plugin.load()
|
||||
self.__plugins.update({file[:-3]: plugin})
|
||||
|
@ -7,7 +7,6 @@ from fastapi.exceptions import RequestValidationError
|
||||
from starlette import status
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.responses import JSONResponse
|
||||
from uvicorn.config import LOGGING_CONFIG
|
||||
|
||||
import core.utils
|
||||
from . import utils
|
||||
@ -21,30 +20,6 @@ uvserver = None
|
||||
data_pool = []
|
||||
data_run = [True]
|
||||
|
||||
LOGGING_CONFIG["formatters"]["default"]['fmt'] = core.utils.log_format
|
||||
LOGGING_CONFIG["formatters"]["access"]["fmt"] = core.utils.log_format_access
|
||||
LOGGING_CONFIG["formatters"].update({
|
||||
"file_default": {
|
||||
"fmt": core.utils.log_format
|
||||
},
|
||||
"file_access": {
|
||||
"fmt": core.utils.log_format_access
|
||||
}
|
||||
})
|
||||
LOGGING_CONFIG["handlers"]["default"]['stream'] = "ext://sys.stdout"
|
||||
LOGGING_CONFIG["handlers"].update({
|
||||
"file_default": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": "webserver.log"
|
||||
},
|
||||
"file_access": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": "webserver.log"
|
||||
}
|
||||
})
|
||||
LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file_default")
|
||||
LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file_access")
|
||||
|
||||
|
||||
def response(data=None, code=status.HTTP_200_OK, error_code=0, error_message=None):
|
||||
if 200 >= code <= 300:
|
||||
@ -78,6 +53,7 @@ async def _method(method, secret_key: str = None):
|
||||
|
||||
async def _stop():
|
||||
await asyncio.sleep(1)
|
||||
if uvserver is not None:
|
||||
uvserver.should_exit = True
|
||||
data_run[0] = False
|
||||
|
||||
|
@ -2,10 +2,17 @@ import asyncio
|
||||
import sys
|
||||
|
||||
import click
|
||||
from uvicorn.server import Server, logger
|
||||
import uvicorn.server as uvs
|
||||
from uvicorn.config import LOGGING_CONFIG
|
||||
|
||||
from uvicorn.lifespan import on
|
||||
|
||||
import core.utils
|
||||
|
||||
# logger = core.utils.get_logger("uvicorn")
|
||||
# uvs.logger = logger
|
||||
logger = uvs.logger
|
||||
|
||||
|
||||
def ev_log_started_message(self, listeners) -> None:
|
||||
cfg = self.config
|
||||
@ -42,7 +49,7 @@ async def ev_shutdown(self, sockets=None) -> None:
|
||||
try:
|
||||
await asyncio.wait_for(self._wait_tasks_to_complete(), timeout=self.config.timeout_graceful_shutdown)
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("Cancel %s running task(s), timeout graceful shutdown exceeded",len(self.server_state.tasks))
|
||||
logger.error("Cancel %s running task(s), timeout graceful shutdown exceeded", len(self.server_state.tasks))
|
||||
for t in self.server_state.tasks:
|
||||
if sys.version_info < (3, 9):
|
||||
t.cancel()
|
||||
@ -81,7 +88,40 @@ async def on_shutdown(self) -> None:
|
||||
|
||||
|
||||
def hack_fastapi():
|
||||
Server.shutdown = ev_shutdown
|
||||
Server._log_started_message = ev_log_started_message
|
||||
uvs.Server.shutdown = ev_shutdown
|
||||
uvs.Server._log_started_message = ev_log_started_message
|
||||
on.LifespanOn.startup = on_startup
|
||||
on.LifespanOn.shutdown = on_shutdown
|
||||
|
||||
LOGGING_CONFIG["formatters"]["default"]['fmt'] = core.utils.log_format
|
||||
LOGGING_CONFIG["formatters"]["access"]["fmt"] = core.utils.log_format
|
||||
LOGGING_CONFIG["formatters"].update({
|
||||
"file_default": {
|
||||
"()": "logging.Formatter",
|
||||
"fmt": core.utils.log_format
|
||||
},
|
||||
"file_access": {
|
||||
"()": "logging.Formatter",
|
||||
"fmt": core.utils.log_format
|
||||
}
|
||||
})
|
||||
LOGGING_CONFIG["handlers"]["default"]['stream'] = "ext://sys.stdout"
|
||||
LOGGING_CONFIG["handlers"].update({
|
||||
"file_default": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": "./logs/web.log",
|
||||
"encoding": "utf-8",
|
||||
"formatter": "file_default"
|
||||
},
|
||||
"file_access": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": "./logs/web_access.log",
|
||||
"encoding": "utf-8",
|
||||
"formatter": "file_access"
|
||||
}
|
||||
})
|
||||
LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file_default")
|
||||
LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file_access")
|
||||
print(LOGGING_CONFIG)
|
||||
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
"stop": "Сервер остановлен!",
|
||||
|
||||
"": "Server auth",
|
||||
"auth_need_key": "Нужен BEAMP ключ для запуска!",
|
||||
"auth_empty_key": "BEAMP ключ пустой!",
|
||||
"auth_need_key": "Нужен BeamMP ключ для запуска!",
|
||||
"auth_empty_key": "BeamMP ключ пустой!",
|
||||
"auth_cannot_open_browser": "Не получилось открыть браузер: {}",
|
||||
"auth_use_link": "Используй эту ссылку: {}",
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
"GUI_no": "Нет",
|
||||
"GUI_ok": "Окей",
|
||||
"GUI_cancel": "Отмена",
|
||||
"GUI_need_key_message": "Нужен BEAMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
|
||||
"GUI_need_key_message": "Нужен BeamMP ключ для запуска!\nХотите открыть ссылку в браузере для получения ключа?",
|
||||
"GUI_enter_key_message": "Пожалуйста введите ключ:",
|
||||
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user