10 Commits

Author SHA1 Message Date
b2a608d369 [+] Add supports for pip packets 2024-07-26 17:07:32 +03:00
c12a91bf86 [+] preparations for pip 2024-07-25 18:58:53 +03:00
209004c9cb [+] pip-packets 2024-07-25 18:30:36 +03:00
2af4681082 [+] Support pip packages 2024-07-25 17:32:00 +03:00
f1f80cc94c [!] FIX unicycle
[!] FIX BeamMP bug
[~] Minor
2024-07-25 17:00:09 +03:00
06942e8a71 [!] Fix open 2024-07-25 12:36:20 +03:00
51867d526d [+] onPlayerReady
[+] ResetCar
2024-07-25 05:07:35 +03:00
ff58e2a994 [minor] 2024-07-25 03:33:46 +03:00
4c6a240f96 [!] Fastfix
[+] call_async_event onPlayerAuthenticated
2024-07-25 02:54:20 +03:00
666a76201e [!] Fastfix 2024-07-25 01:01:42 +03:00
8 changed files with 55 additions and 19 deletions

1
.gitignore vendored
View File

@@ -143,3 +143,4 @@ logs/
/win-ver_info.txt /win-ver_info.txt
/output/ /output/
pip-packets/

View File

@@ -130,7 +130,7 @@ class Client:
if not message: if not message:
message = "no message" message = "no message"
to_all = False to_all = False
await self._send(f"C:{message!r}", to_all=to_all) await self._send(f"C:{message}", to_all=to_all)
async def send_event(self, event_name, event_data, to_all=True): async def send_event(self, event_name, event_data, to_all=True):
if isinstance(event_data, (list, tuple, dict)): if isinstance(event_data, (list, tuple, dict)):
@@ -504,10 +504,10 @@ class Client:
pass pass
if cid == self.cid or allow or admin_allow: if cid == self.cid or allow or admin_allow:
if car['snowman']: if car['unicycle']:
unicycle_id = self._unicycle['id'] unicycle_id = self._unicycle['id']
self._unicycle['id'] = -1 self._unicycle['id'] = -1
self.log.debug(f"Delete snowman") self.log.debug(f"Delete unicycle")
await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True) await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True)
self._cars[unicycle_id] = None self._cars[unicycle_id] = None
else: else:
@@ -521,10 +521,11 @@ class Client:
self.log.debug(f"Invalid car: car_id={car_id}") self.log.debug(f"Invalid car: car_id={car_id}")
async def reset_car(self, car_id, x, y, z, rot=None): async def reset_car(self, car_id, x, y, z, rot=None):
# TODO: reset_car self.log.debug(f"Resetting car from plugin {x, y, z}; {rot=}")
self.log.debug(f"Resetting car from plugin") jpkt = {"pos": {"y": float(y), "x": float(x), "z": float(z)}, "rot": {"y": 0, "w": 0, "x": 0, "z": 0}}
if rot is None: if rot:
rot = {"y": 0, "w": 0, "x": 0, "z": 0} jpkt['rot'] = rot
await self._send(f"Or:{self.cid}-{car_id}:{json.dumps(jpkt)}", True)
async def _reset_car(self, raw_data): async def _reset_car(self, raw_data):
cid, car_id = self._get_cid_vid(raw_data) cid, car_id = self._get_cid_vid(raw_data)
@@ -545,6 +546,7 @@ class Client:
async def _handle_car_codes(self, raw_data): async def _handle_car_codes(self, raw_data):
if len(raw_data) < 6: if len(raw_data) < 6:
return return
self.log.debug(f"[car] {raw_data}")
sub_code = raw_data[1] sub_code = raw_data[1]
data = raw_data[3:] data = raw_data[3:]
match sub_code: match sub_code:
@@ -602,6 +604,8 @@ class Client:
self.log.info(i18n.client_sync_time.format(round(time.monotonic() - self._connect_time, 2))) self.log.info(i18n.client_sync_time.format(round(time.monotonic() - self._connect_time, 2)))
self._ready = True self._ready = True
ev.call_event("onPlayerReady", player=self)
await ev.call_async_event("onPlayerReady", player=self)
async def _chat_handler(self, data): async def _chat_handler(self, data):
sup = data.find(":", 2) sup = data.find(":", 2)

View File

@@ -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.4.6' __version__ = '0.4.7'
__build__ = 2421 # Я это считаю лог файлами __build__ = 2469 # Я это считаю лог файлами
__author__ = 'SantaSpeen' __author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su' __author_email__ = 'admin@kuitoi.su'
__license__ = "FPA" __license__ = "FPA"

View File

@@ -54,7 +54,7 @@ class Core:
ev.register("_get_player", lambda x: self.get_client(**x['kwargs'])) ev.register("_get_player", lambda x: self.get_client(**x['kwargs']))
def get_client(self, cid=None, nick=None): def get_client(self, cid=None, nick=None):
if cid is None and nick is None: if (cid, nick) == (None, None):
return None return None
if cid is not None: if cid is not None:
if cid == -1: if cid == -1:
@@ -160,11 +160,16 @@ class Core:
BEAM_backend = ["backend.beammp.com", "backup1.beammp.com", "backup2.beammp.com"] BEAM_backend = ["backend.beammp.com", "backup1.beammp.com", "backup2.beammp.com"]
_map = config.Game['map'] if "/" in config.Game['map'] else f"/levels/{config.Game['map']}/info.json" _map = config.Game['map'] if "/" in config.Game['map'] else f"/levels/{config.Game['map']}/info.json"
tags = config.Server['tags'].replace(", ", ";").replace(",", ";") tags = config.Server['tags'].replace(", ", ";").replace(",", ";")
self.log.debug(f"[heartbeat] {_map=}")
self.log.debug(f"[heartbeat] {tags=}")
if tags and tags[-1:] != ";": if tags and tags[-1:] != ";":
tags += ";" tags += ";"
modlist = "".join(f"/{os.path.basename(mod['path'])};" for mod in self.mods_list[1:]) modlist = "".join(f"/{os.path.basename(mod['path'])};" for mod in self.mods_list[1:])
modstotalsize = self.mods_list[0] modstotalsize = self.mods_list[0]
modstotal = len(self.mods_list) - 1 modstotal = len(self.mods_list) - 1
self.log.debug(f"[heartbeat] {modlist=}")
self.log.debug(f"[heartbeat] {modstotalsize=}")
self.log.debug(f"[heartbeat] {modstotal=}")
while self.run: while self.run:
playerslist = "".join(f"{client.nick};" for client in self.clients if client and client.alive) playerslist = "".join(f"{client.nick};" for client in self.clients if client and client.alive)
data = { data = {

View File

@@ -35,12 +35,11 @@ class TCPServer:
await client.kick(i18n.core_player_kick_outdated) await client.kick(i18n.core_player_kick_outdated)
return False, client return False, client
else: else:
# await client._send(b"S") # Accepted client version
await client._send(b"A") # Accepted client version await client._send(b"A") # Accepted client version
data = await client._recv(True) data = await client._recv(True)
self.log.debug(f"Key: {data}") self.log.debug(f"Key: {data}")
if len(data) > 50: if not data or len(data) > 50:
await client.kick(i18n.core_player_kick_bad_key) await client.kick(i18n.core_player_kick_bad_key)
return False, client return False, client
client._key = data.decode("utf-8") client._key = data.decode("utf-8")
@@ -89,6 +88,10 @@ class TCPServer:
return False, client return False, client
ev.call_event("onPlayerAuthenticated", player=client) ev.call_event("onPlayerAuthenticated", player=client)
await ev.call_async_event("onPlayerAuthenticated", player=client)
if not client.alive:
await client.kick("Not accepted.")
return False, client
if len(self.Core.clients_by_id) > config.Game["players"]: if len(self.Core.clients_by_id) > config.Game["players"]:
await client.kick(i18n.core_player_kick_server_full) await client.kick(i18n.core_player_kick_server_full)

View File

@@ -36,7 +36,7 @@ class UDPServer(asyncio.DatagramTransport):
client = self._core.get_client(cid=cid) client = self._core.get_client(cid=cid)
if client: if client:
if not client.alive: if not client.alive:
self.log.debug(f"{client.nick}:{cid} still sending UDP data: {data}") client.log.debug(f"Still sending UDP data: {data}")
match code: match code:
case "p": # Ping packet case "p": # Ping packet
ev.call_event("onSentPing") ev.call_event("onSentPing")
@@ -45,7 +45,6 @@ class UDPServer(asyncio.DatagramTransport):
if client._udp_sock != (self.transport, addr): if client._udp_sock != (self.transport, addr):
client._udp_sock = (self.transport, addr) client._udp_sock = (self.transport, addr)
self.log.debug(f"Set UDP Sock for CID: {cid}") self.log.debug(f"Set UDP Sock for CID: {cid}")
ev.call_event("onChangePosition", data=data)
sub = data.find("{", 1) sub = data.find("{", 1)
last_pos = data[sub:] last_pos = data[sub:]
try: try:
@@ -54,6 +53,7 @@ class UDPServer(asyncio.DatagramTransport):
last_pos = json.loads(last_pos) last_pos = json.loads(last_pos)
client._last_position = last_pos client._last_position = last_pos
client._cars[car_id]['pos'] = last_pos client._cars[car_id]['pos'] = last_pos
ev.call_event("onChangePosition", data, player=client, pos=last_pos)
except Exception as e: except Exception as e:
self.log.warning(f"Cannot parse position packet: {e}") self.log.warning(f"Cannot parse position packet: {e}")
self.log.debug(f"data: '{data}', sup: {sub}") self.log.debug(f"data: '{data}', sup: {sub}")
@@ -62,7 +62,7 @@ class UDPServer(asyncio.DatagramTransport):
case "X": case "X":
await client._send(data, to_all=True, to_self=False, to_udp=True) await client._send(data, to_all=True, to_self=False, to_udp=True)
case _: case _:
self.log.warning(f" UDP [{cid}] Unknown code: {code}; {data}") self.log.warning(f"UDP [{cid}] Unknown code: {code}; {data}")
else: else:
self.log.debug(f"[{cid}] Client not found.") self.log.debug(f"[{cid}] Client not found.")

View File

@@ -26,6 +26,7 @@ class EventsSystem:
"onPlayerSentKey": [], # Only sync, no handler "onPlayerSentKey": [], # Only sync, no handler
"onPlayerAuthenticated": [], # (!) Only sync, With handler "onPlayerAuthenticated": [], # (!) Only sync, With handler
"onPlayerJoin": [], # (!) With handler "onPlayerJoin": [], # (!) With handler
"onPlayerReady": [], # No handler
"onChatReceive": [], # (!) With handler "onChatReceive": [], # (!) With handler
"onCarSpawn": [], # (!) With handler "onCarSpawn": [], # (!) With handler
"onCarDelete": [], # (!) With handler (admin allow) "onCarDelete": [], # (!) With handler (admin allow)
@@ -41,6 +42,7 @@ class EventsSystem:
self.__async_events = { self.__async_events = {
"onServerStarted": [], "onServerStarted": [],
"onPlayerJoin": [], "onPlayerJoin": [],
"onPlayerReady": [],
"onChatReceive": [], "onChatReceive": [],
"onCarSpawn": [], "onCarSpawn": [],
"onCarDelete": [], "onCarDelete": [],

View File

@@ -9,8 +9,11 @@
import asyncio import asyncio
import inspect import inspect
import os import os
import subprocess
import sys
import types import types
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from threading import Thread from threading import Thread
from core import get_logger from core import get_logger
@@ -24,8 +27,8 @@ class KuiToi:
raise AttributeError("KuiToi: Name is required") raise AttributeError("KuiToi: Name is required")
self.__log = get_logger(f"Plugin | {name}") self.__log = get_logger(f"Plugin | {name}")
self.__name = name self.__name = name
self.__dir = os.path.join(self._plugins_dir, self.__name) self.__dir = Path(self._plugins_dir) / self.__name
if not os.path.exists(self.__dir): if not self.__dir.exists():
os.mkdir(self.__dir) os.mkdir(self.__dir)
@property @property
@@ -42,7 +45,9 @@ class KuiToi:
@contextmanager @contextmanager
def open(self, file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None): 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) path = self.__dir / file
if str(self.__dir) in str(file):
path = file
self.log.debug(f'Trying to open "{path}" with mode "{mode}"') self.log.debug(f'Trying to open "{path}" with mode "{mode}"')
# Really need? # Really need?
# if not os.path.exists(path): # if not os.path.exists(path):
@@ -97,6 +102,7 @@ class KuiToi:
class PluginsLoader: class PluginsLoader:
_pip_dir = str(Path("pip-packets").resolve())
def __init__(self, plugins_dir): def __init__(self, plugins_dir):
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
@@ -110,6 +116,21 @@ class PluginsLoader:
ev.register("_plugins_get", lambda x: list(self.plugins.keys())) ev.register("_plugins_get", lambda x: list(self.plugins.keys()))
console.add_command("plugins", lambda x: self.loaded_str[:-2]) console.add_command("plugins", lambda x: self.loaded_str[:-2])
console.add_command("pl", lambda x: self.loaded_str[:-2]) console.add_command("pl", lambda x: self.loaded_str[:-2])
sys.path.append(self._pip_dir)
os.makedirs(self._pip_dir, exist_ok=True)
console.add_command("install", self._pip_install)
def _pip_install(self, x):
self.log.debug(f"_pip_install {x}")
if len(x) > 0:
try:
subprocess.check_call(['pip', 'install', *x, '--target', self._pip_dir])
return "Success"
except subprocess.CalledProcessError as e:
self.log.debug(f"error: {e}")
return f"Failed to install packages"
else:
return "Invalid syntax"
async def load(self): async def load(self):
self.log.debug("Loading plugins...") self.log.debug("Loading plugins...")