mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 08:15:42 +00:00
Compare commits
5 Commits
3701bc441c
...
30b6a71a1c
Author | SHA1 | Date | |
---|---|---|---|
30b6a71a1c | |||
b321595e7e | |||
10e03b2582 | |||
a9774a63cd | |||
eb28a4d5cf |
@ -7,9 +7,9 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
||||
## TODOs
|
||||
|
||||
- [ ] Server core
|
||||
- [ ] BEAMP System
|
||||
- [x] Private access without key
|
||||
- [ ] Server authentication (For public access)
|
||||
- [x] BEAMP System
|
||||
- [x] Private access without key (Direct connect)
|
||||
- [x] Server authentication (For public access)
|
||||
- [X] Player authentication
|
||||
- [ ] TCP Server part:
|
||||
- [x] Handle code
|
||||
@ -34,6 +34,8 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
||||
- [x] Create custom events
|
||||
- [ ] Return from events
|
||||
- [x] Plugins support
|
||||
- [x] Load Python plugins
|
||||
- [ ] Load Lua plugins (Original BEAMP compatibility)
|
||||
- [x] MultiLanguage (i18n support)
|
||||
- [x] Core
|
||||
- [x] Console
|
||||
|
@ -21,6 +21,9 @@
|
||||
"GUI_enter_key_message": "Please type your key:",
|
||||
"GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}",
|
||||
|
||||
"": "Web phases",
|
||||
"web_start": "WebAPI running on {} (Press CTRL+C to quit)",
|
||||
|
||||
"": "Command: man",
|
||||
"man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND",
|
||||
"help_message_man": "Display the manual page for COMMAND.",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"": "Basic phases",
|
||||
"hello": "Привет из KuiToi-Server!",
|
||||
"config_path": "Используя {} для настройки.",
|
||||
"config_path": "Используй {} для настройки.",
|
||||
"init_ok": "Инициализация окончена.",
|
||||
"start": "Сервер запущен!",
|
||||
"stop": "Сервер остановлен!",
|
||||
@ -21,6 +21,9 @@
|
||||
"GUI_enter_key_message": "Пожалуйста введите ключ:",
|
||||
"GUI_cannot_open_browser": "Не получилось открыть браузер.\nИспользуй эту ссылку: {}",
|
||||
|
||||
"": "Web phases",
|
||||
"web_start": "WebAPI запустился на {} (CTRL+C для выключения)",
|
||||
|
||||
"": "Command: man",
|
||||
"man_message_man": "man - Показывает страничку помощи для COMMAND.\nИспользование: man COMMAND",
|
||||
"help_message_man": "Показывает страничку помощи для COMMAND.",
|
||||
|
@ -45,6 +45,7 @@ if args.config:
|
||||
config_path = args.config
|
||||
config_provider = ConfigProvider(config_path)
|
||||
config = config_provider.open_config()
|
||||
builtins.config = config
|
||||
if config.Server['debug'] is True:
|
||||
utils.set_debug_status()
|
||||
log.info("Debug enabled!")
|
||||
@ -67,8 +68,7 @@ log.info(i18n.config_path.format(config_path))
|
||||
|
||||
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:
|
||||
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(
|
||||
@ -93,11 +93,10 @@ if not private:
|
||||
ok_text=i18n.GUI_ok,
|
||||
cancel_text=i18n.GUI_cancel).run()
|
||||
config_provider.save_config()
|
||||
if not private:
|
||||
if not config.Auth['private'] and not config.Auth['key']:
|
||||
log.error(i18n.auth_empty_key)
|
||||
log.info(i18n.stop)
|
||||
exit(1)
|
||||
builtins.config = config
|
||||
|
||||
# Console Init
|
||||
log.debug("Initializing console...")
|
||||
@ -107,9 +106,6 @@ console.builtins_hook()
|
||||
console.add_command("stop", console.stop, i18n.man_message_stop, i18n.help_message_stop)
|
||||
console.add_command("exit", console.stop, i18n.man_message_exit, i18n.help_message_exit)
|
||||
|
||||
if not os.path.exists("mods"):
|
||||
os.mkdir("mods")
|
||||
|
||||
log.debug("Initializing PluginsLoader...")
|
||||
if not os.path.exists("plugins"):
|
||||
os.mkdir("plugins")
|
||||
|
135
src/core/core.py
135
src/core/core.py
@ -5,16 +5,19 @@
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
import zlib
|
||||
from threading import Thread
|
||||
|
||||
import aiohttp
|
||||
import uvicorn
|
||||
|
||||
from core import utils
|
||||
from modules.WebAPISystem import app as webapp
|
||||
from core.tcp_server import TCPServer
|
||||
from core.udp_server import UDPServer
|
||||
from modules.WebAPISystem import app as webapp
|
||||
|
||||
|
||||
class Client:
|
||||
@ -144,8 +147,12 @@ class Core:
|
||||
def __init__(self):
|
||||
self.log = utils.get_logger("core")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.run = False
|
||||
self.direct = False
|
||||
self.clients = {}
|
||||
self.clients_counter = 0
|
||||
self.mods_dir = "./mods"
|
||||
self.mods_list = [0, ]
|
||||
self.server_ip = config.Server["server_ip"]
|
||||
self.server_port = config.Server["server_port"]
|
||||
self.tcp = TCPServer
|
||||
@ -154,6 +161,9 @@ class Core:
|
||||
self.web_pool = webapp.data_pool
|
||||
self.web_stop = None
|
||||
|
||||
self.client_major_version = "2.0"
|
||||
self.BEAMP_version = "3.2.0"
|
||||
|
||||
def get_client(self, sock=None, cid=None):
|
||||
if cid:
|
||||
return self.clients.get(cid)
|
||||
@ -197,37 +207,132 @@ class Core:
|
||||
await asyncio.sleep(1)
|
||||
raise KeyboardInterrupt
|
||||
|
||||
# noinspection SpellCheckingInspection,PyPep8Naming
|
||||
async def authenticate(self, test=False):
|
||||
if config.Auth["private"] or self.direct:
|
||||
if test:
|
||||
self.log.info(f"Server runnig in Direct connect 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:
|
||||
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, "clientversion": self.client_major_version,
|
||||
"name": config.Server["name"], "modlist": modlist, "modstotalsize": modstotalsize,
|
||||
"modstotal": modstotal, "playerslist": "", "desc": config.Server['description'], "pass": False}
|
||||
self.log.debug(f"Auth: data {data}")
|
||||
|
||||
# Sentry?
|
||||
ok = False
|
||||
body = {}
|
||||
code = 0
|
||||
for server_url in BEAM_backend:
|
||||
url = "https://" + server_url + "/heartbeat"
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, data=data, headers={"api-v": "2"}) as response:
|
||||
code = response.status
|
||||
body = await response.json()
|
||||
self.log.debug(f"Auth: code {code}, body {body}")
|
||||
ok = True
|
||||
break
|
||||
except Exception as e:
|
||||
self.log.debug(f"Auth: Error `{e}` while auth with `{server_url}`")
|
||||
continue
|
||||
|
||||
if ok:
|
||||
if not (body.get("status") is not None and
|
||||
body.get("code") is not None and
|
||||
body.get("msg") is not None):
|
||||
self.log.error("Missing/invalid json members in backend response")
|
||||
raise KeyboardInterrupt
|
||||
|
||||
if test:
|
||||
status = body.get("status")
|
||||
msg = body.get("msg")
|
||||
if status == "2000":
|
||||
self.log.info(f"Authenticated! {msg}")
|
||||
elif status == "200":
|
||||
self.log.info(f"Resumed authenticated session. {msg}")
|
||||
else:
|
||||
self.log.error(f"Backend REFUSED the auth key. Reason: "
|
||||
f"{msg or 'Backend did not provide a reason'}")
|
||||
self.log.info(f"Server still runnig, but only in Direct connect mode.")
|
||||
self.direct = True
|
||||
else:
|
||||
self.direct = True
|
||||
if test:
|
||||
self.log.error("Cannot auth...")
|
||||
if not config.Auth['private']:
|
||||
raise KeyboardInterrupt
|
||||
if test:
|
||||
self.log.info(f"Server still runnig, but only in Direct connect mode.")
|
||||
|
||||
if test:
|
||||
return ok
|
||||
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def main(self):
|
||||
try:
|
||||
self.run = True
|
||||
self.tcp = self.tcp(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.stop_me()] # self.check_alive()
|
||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||
|
||||
# WebApi Start
|
||||
if config.WebAPI["enabled"]:
|
||||
self.log.debug("Initializing WebAPI...")
|
||||
web_thread = Thread(target=self.start_web)
|
||||
web_thread.start()
|
||||
self.web_thread = web_thread
|
||||
self.web_stop = webapp._stop
|
||||
|
||||
# Mods handler
|
||||
self.log.debug("Listing mods..")
|
||||
if not os.path.exists(self.mods_dir):
|
||||
os.mkdir(self.mods_dir)
|
||||
for file in os.listdir(self.mods_dir):
|
||||
path = os.path.join(self.mods_dir, file).replace("\\", "/")
|
||||
if os.path.isfile(path) and path.endswith(".zip"):
|
||||
size = os.path.getsize(path)
|
||||
self.mods_list.append({"path": path, "size": size})
|
||||
self.mods_list[0] += size
|
||||
self.log.debug(f"mods_list: {self.mods_list}")
|
||||
lmods = len(self.mods_list) - 1
|
||||
if lmods > 0:
|
||||
self.log.info(f"Loaded {lmods} mods: {round(self.mods_list[0] / MB, 2)}mb")
|
||||
|
||||
await self.authenticate(True)
|
||||
tasks = [self.tcp.start(), self.udp.start(), console.start(),
|
||||
self.stop_me(), self.authenticate(),] # self.check_alive()
|
||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||
|
||||
self.log.info(i18n.start)
|
||||
# TODO: Server auth
|
||||
ev.call_event("on_started")
|
||||
await t
|
||||
# while True:
|
||||
# try:
|
||||
# tasks = [console.start(), self.tcp.start(), self.udp.start()] # self.check_alive()
|
||||
# await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||
# except Exception as e:
|
||||
# await asyncio.sleep(1)
|
||||
# print("Error: " + str(e))
|
||||
# traceback.print_exc()
|
||||
# break
|
||||
# except KeyboardInterrupt:
|
||||
# raise KeyboardInterrupt
|
||||
# Wait the end.
|
||||
except Exception as e:
|
||||
self.log.error(f"Exception: {e}")
|
||||
traceback.print_exc()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
self.run = False
|
||||
|
||||
def start(self):
|
||||
asyncio.run(self.main())
|
||||
|
||||
def stop(self):
|
||||
self.run = False
|
||||
self.log.info(i18n.stop)
|
||||
asyncio.run(self.web_stop())
|
||||
exit(0)
|
||||
|
@ -40,23 +40,30 @@ class Client:
|
||||
|
||||
class Core:
|
||||
def __init__(self):
|
||||
self.clients_counter: int = 0
|
||||
self.log = utils.get_logger("core")
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.run = False
|
||||
self.direct = False
|
||||
self.clients = dict()
|
||||
self.clients_counter: int = 0
|
||||
self.mods_dir: str = "mods"
|
||||
self.mods_list: list = []
|
||||
self.server_ip = config.Server["server_ip"]
|
||||
self.server_port = config.Server["server_port"]
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.tcp = TCPServer
|
||||
self.udp = UDPServer
|
||||
self.web_thread: Thread = None
|
||||
self.web_stop: Callable = lambda: None
|
||||
self.client_major_version = "2.0"
|
||||
self.BEAMP_version = "3.2.0"
|
||||
def insert_client(self, client: Client) -> None: ...
|
||||
def create_client(self, *args, **kwargs) -> Client: ...
|
||||
async def check_alive(self) -> None: ...
|
||||
@staticmethod
|
||||
def start_web() -> None: ...
|
||||
@staticmethod
|
||||
def stop_me(self) -> None: ...
|
||||
def stop_me() -> None: ...
|
||||
async def authenticate(self, test=False) -> None: ...
|
||||
async def main(self) -> None: ...
|
||||
def start(self) -> None: ...
|
||||
def stop(self) -> None: ...
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"": "Basic phases",
|
||||
"hello": "Привет из KuiToi-Server!",
|
||||
"config_path": "Используя {} для настройки.",
|
||||
"config_path": "Используй {} для настройки.",
|
||||
"init_ok": "Инициализация окончена.",
|
||||
"start": "Сервер запущен!",
|
||||
"stop": "Сервер остановлен!",
|
||||
|
Loading…
x
Reference in New Issue
Block a user