Compare commits

..

No commits in common. "5f8b70a2ee14e97be5b53a7c802a0458105bf72b" and "a5202edf8356a697a89857157a87e6617e509b4f" have entirely different histories.

8 changed files with 36 additions and 137 deletions

View File

@ -41,11 +41,9 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [x] Call events
- [x] Create custom events
- [x] Return from events
- [x] Async support
- [x] Plugins support
- [ ] KuiToi class
- [x] Load Python plugins
- [x] Async support
- [ ] Load Lua plugins (Original BeamMP compatibility)
- [x] MultiLanguage (i18n support)
- [x] Core

View File

@ -70,7 +70,7 @@ class Client:
if len(data) == 10:
data += b"."
header = len(data).to_bytes(4, "little", signed=True)
self.log.debug(f'len: {len(data)}; send: {header + data!r}')
self.log.debug(f'len: {len(data)}; send: {header + data}')
try:
writer.write(header + data)
await writer.drain()
@ -116,7 +116,6 @@ class Client:
self.alive = False
return b""
# TODO: Speed limiter
async def _split_load(self, start, end, d_sock, filename):
real_size = end - start
writer = self.down_rw[1] if d_sock else self.writer

View File

@ -11,7 +11,7 @@ __title__ = 'KuiToi-Server'
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
__version__ = '0.2.2'
__build__ = 1176 # Я это считаю лог файлами
__build__ = 1113 # Я это считаю лог файлами
__author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su'
__license__ = "FPA"
@ -19,6 +19,7 @@ __copyright__ = 'Copyright 2023 © SantaSpeen (Maxim Khomutov)'
import asyncio
import builtins
import os
import webbrowser
import prompt_toolkit.shortcuts as shortcuts
@ -26,7 +27,7 @@ import prompt_toolkit.shortcuts as shortcuts
from .utils import get_logger
from core.core import Core
from main import parser
from modules import ConfigProvider, EventsSystem
from modules import ConfigProvider, EventsSystem, PluginsLoader
from modules import Console
from modules import MultiLanguage
@ -106,8 +107,16 @@ 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)
log.debug("Initializing PluginsLoader...")
if not os.path.exists("plugins"):
os.mkdir("plugins")
pl = PluginsLoader("plugins")
pl.load_plugins()
builtins.B = 1
builtins.KB = B * 1024
builtins.MB = KB * 1024
builtins.GB = MB * 1024
builtins.TB = GB * 1024
log.info(i18n.init_ok)

View File

@ -16,7 +16,6 @@ from core import utils
from core.Client import Client
from core.tcp_server import TCPServer
from core.udp_server import UDPServer
from modules import PluginsLoader
from modules.WebAPISystem import app as webapp
@ -200,19 +199,13 @@ class Core:
self.log.error(f"Error in heartbeat: {e}")
async def main(self):
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)
console.add_command(
"list",
lambda x: f"Players list: {self.get_clients_list(True)}"
)
self.log.debug("Initializing PluginsLoader...")
if not os.path.exists("plugins"):
os.mkdir("plugins")
pl = PluginsLoader("plugins")
await pl.load()
try:
# WebApi Start
if config.WebAPI["enabled"]:
@ -240,7 +233,6 @@ class Core:
if len_mods > 0:
# TODO: i18n
self.log.info(f"Loaded {len_mods} mods: {round(self.mods_list[0] / MB, 2)}mb")
self.log.info(i18n.init_ok)
await self.heartbeat(True)
for i in range(int(config.Game["players"] * 2.3)): # * 2.3 For down sock and buffer.
@ -252,11 +244,8 @@ class Core:
tasks.append(asyncio.create_task(task()))
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
ev.call_event("_plugins_start")
self.run = True
self.log.info(i18n.start)
ev.call_event("server_started")
ev.call_event("on_started")
await t # Wait end.
except Exception as e:
self.log.error(f"Exception: {e}")
@ -272,8 +261,6 @@ class Core:
asyncio.run(self.main())
def stop(self):
ev.call_event("server_stopped")
ev.call_event("_plugins_unload")
self.run = False
self.log.info(i18n.stop)
if config.WebAPI["enabled"]:

View File

@ -1,7 +1,4 @@
import asyncio
import builtins
import inspect
import time
from core import get_logger
@ -11,17 +8,13 @@ class EventsSystem:
def __init__(self):
# TODO: default events
self.log = get_logger("EventsSystem")
self.loop = asyncio.get_event_loop()
self.__events = {
"server_started": [],
"_plugins_start": [],
"on_started": [],
"auth_sent_key": [],
"auth_ok": [],
"chat_receive": [],
"_plugins_unload": [],
"server_stopped": [],
}
self.log = get_logger("EventsSystem")
def builtins_hook(self):
self.log.debug("used builtins_hook")
@ -46,12 +39,7 @@ class EventsSystem:
if event_name in self.__events.keys():
for func in self.__events[event_name]:
try:
event_data = {"event_name": event_name, "args": args, "kwargs": kwargs}
if inspect.iscoroutinefunction(func):
d = self.loop.run_until_complete(func(event_data))
else:
d = func(event_data)
funcs_data.append(d)
funcs_data.append(func({"event_name": event_name, "args": args, "kwargs": kwargs}))
except Exception as e:
# TODO: i18n
self.log.error(f'Error while calling "{event_name}"; In function: "{func.__name__}"')

View File

@ -1,6 +1,6 @@
class EventsSystem:
@staticmethod
def register_event(event_name, event_func): ...
def register_event(self, event_name, event_func): ...
@staticmethod
def call_event(event_name, *data, **kwargs): ...
class ev(EventsSystem): ...
def call_event(self, event_name, *data): ...
class ev(EventsSystem): ...

View File

@ -1,9 +1,6 @@
import asyncio
import inspect
import os
import types
from contextlib import contextmanager
from threading import Thread
from core import get_logger
@ -42,9 +39,8 @@ class KuiToi:
@contextmanager
def open(self, file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None):
path = os.path.join(self.__dir, file)
self.log.debug(f'Trying to open "{path}" with mode "{mode}"')
# if not os.path.exists(path):
# with open(path, 'x'): ...
if not os.path.exists(path):
with open(path, 'x'): ...
f = None
try:
f = open(path, mode, buffering, encoding, errors, newline, closefd, opener)
@ -55,27 +51,23 @@ class KuiToi:
if f is not None:
f.close()
def register_event(self, event_name, event_func):
self.log.debug(f"Registering event {event_name}")
@staticmethod
def register_event(event_name, event_func):
ev.register_event(event_name, event_func)
def call_event(self, event_name, *data, **kwargs):
self.log.debug(f"Called event {event_name}")
ev.call_event(event_name, *data, **kwargs)
@staticmethod
def call_event(event_name, *data):
ev.call_event(event_name, *data)
class PluginsLoader:
def __init__(self, plugins_dir):
self.loop = asyncio.get_event_loop()
self.plugins = {}
self.plugins_tasks = []
self.plugins_dir = plugins_dir
self.log = get_logger("PluginsLoader")
ev.register_event("_plugins_start", self.start)
ev.register_event("_plugins_unload", self.unload)
async def load(self):
def load_plugins(self):
self.log.debug("Loading plugins...")
files = os.listdir(self.plugins_dir)
for file in files:
@ -85,99 +77,24 @@ class PluginsLoader:
plugin = types.ModuleType(file[:-3])
plugin.KuiToi = KuiToi
plugin.KuiToi._plugins_dir = self.plugins_dir
plugin.kt = None
plugin.print = print
file_path = os.path.join(self.plugins_dir, file)
plugin.__file__ = file_path
with open(f'{file_path}', 'r', encoding="utf-8") as f:
with open(f'{file_path}', 'r') as f:
code = f.read()
exec(code, plugin.__dict__)
ok = True
try:
isfunc = inspect.isfunction
if not isfunc(plugin.load):
self.log.error('Function "def load():" not found.')
ok = False
if not isfunc(plugin.start):
self.log.error('Function "def start():" not found.')
ok = False
if not isfunc(plugin.unload):
self.log.error('Function "def unload():" not found.')
ok = False
if type(plugin.kt) != KuiToi:
self.log.error(f'Attribute "kt" isn\'t KuiToi class. Plugin file: "{file_path}"')
ok = False
except AttributeError:
ok = False
if not ok:
self.log.error(f'Plugin file: "{file_path}" is not a valid KuiToi plugin.')
return
if type(plugin.kt) != KuiToi:
raise AttributeError(f'Attribute "kt" isn\'t KuiToi class. Plugin file: "{file_path}"')
pl_name = plugin.kt.name
if self.plugins.get(pl_name) is not None:
raise NameError(f'Having plugins with identical names is not allowed; '
f'Plugin name: "{pl_name}"; Plugin file "{file_path}"')
plugin.open = plugin.kt.open
iscorfunc = inspect.iscoroutinefunction
self.plugins.update(
{
pl_name: {
"plugin": plugin,
"load": {
"func": plugin.load,
"async": iscorfunc(plugin.load)
},
"start": {
"func": plugin.start,
"async": iscorfunc(plugin.start)
},
"unload": {
"func": plugin.unload,
"async": iscorfunc(plugin.unload)
}
}
}
)
if self.plugins[pl_name]["load"]['async']:
plugin.log.debug(f"I'm async")
await plugin.load()
else:
plugin.log.debug(f"I'm sync")
th = Thread(target=plugin.load, name=f"{pl_name}.load()")
th.start()
th.join()
self.log.debug(f"Plugin loaded: {file}. Settings: {self.plugins[pl_name]}")
plugin.load()
self.plugins.update({pl_name: plugin})
self.log.debug(f"Plugin loaded: {file}")
except Exception as e:
# TODO: i18n
self.log.error(f"Error while loading plugin: {file}; Error: {e}")
self.log.exception(e)
async def start(self, _):
for pl_name, pl_data in self.plugins.items():
try:
if pl_data['start']['async']:
self.log.debug(f"Start async plugin: {pl_name}")
t = self.loop.create_task(pl_data['start']['func']())
self.plugins_tasks.append(t)
else:
self.log.debug(f"Start sync plugin: {pl_name}")
th = Thread(target=pl_data['start']['func'], name=f"Thread {pl_name}")
th.start()
self.plugins_tasks.append(th)
except Exception as e:
self.log.exception(e)
async def unload(self, _):
for pl_name, pl_data in self.plugins.items():
try:
if pl_data['unload']['async']:
self.log.debug(f"Unload async plugin: {pl_name}")
await pl_data['unload']['func']()
else:
self.log.debug(f"Unload sync plugin: {pl_name}")
th = Thread(target=pl_data['unload']['func'], name=f"Thread {pl_name}")
th.start()
th.join()
except Exception as e:
self.log.exception(e)

View File

@ -122,5 +122,6 @@ def hack_fastapi():
})
LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("file_default")
LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("file_access")
print(LOGGING_CONFIG)