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

3
.gitignore vendored
View File

@@ -142,4 +142,5 @@ logs/
*.toml
/win-ver_info.txt
/output/
/output/
pip-packets/

View File

@@ -130,7 +130,7 @@ class Client:
if not message:
message = "no message"
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):
if isinstance(event_data, (list, tuple, dict)):
@@ -504,10 +504,10 @@ class Client:
pass
if cid == self.cid or allow or admin_allow:
if car['snowman']:
if car['unicycle']:
unicycle_id = self._unicycle['id']
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)
self._cars[unicycle_id] = None
else:
@@ -521,10 +521,11 @@ class Client:
self.log.debug(f"Invalid car: car_id={car_id}")
async def reset_car(self, car_id, x, y, z, rot=None):
# TODO: reset_car
self.log.debug(f"Resetting car from plugin")
if rot is None:
rot = {"y": 0, "w": 0, "x": 0, "z": 0}
self.log.debug(f"Resetting car from plugin {x, y, z}; {rot=}")
jpkt = {"pos": {"y": float(y), "x": float(x), "z": float(z)}, "rot": {"y": 0, "w": 0, "x": 0, "z": 0}}
if rot:
jpkt['rot'] = rot
await self._send(f"Or:{self.cid}-{car_id}:{json.dumps(jpkt)}", True)
async def _reset_car(self, 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):
if len(raw_data) < 6:
return
self.log.debug(f"[car] {raw_data}")
sub_code = raw_data[1]
data = raw_data[3:]
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._ready = True
ev.call_event("onPlayerReady", player=self)
await ev.call_async_event("onPlayerReady", player=self)
async def _chat_handler(self, data):
sup = data.find(":", 2)

View File

@@ -9,8 +9,8 @@
__title__ = 'KuiToi-Server'
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
__version__ = '0.4.6'
__build__ = 2421 # Я это считаю лог файлами
__version__ = '0.4.7'
__build__ = 2469 # Я это считаю лог файлами
__author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su'
__license__ = "FPA"

View File

@@ -54,7 +54,7 @@ class Core:
ev.register("_get_player", lambda x: self.get_client(**x['kwargs']))
def get_client(self, cid=None, nick=None):
if cid is None and nick is None:
if (cid, nick) == (None, None):
return None
if cid is not None:
if cid == -1:
@@ -160,11 +160,16 @@ class Core:
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"
tags = config.Server['tags'].replace(", ", ";").replace(",", ";")
self.log.debug(f"[heartbeat] {_map=}")
self.log.debug(f"[heartbeat] {tags=}")
if tags and tags[-1:] != ";":
tags += ";"
modlist = "".join(f"/{os.path.basename(mod['path'])};" for mod in self.mods_list[1:])
modstotalsize = self.mods_list[0]
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:
playerslist = "".join(f"{client.nick};" for client in self.clients if client and client.alive)
data = {

View File

@@ -35,12 +35,11 @@ class TCPServer:
await client.kick(i18n.core_player_kick_outdated)
return False, client
else:
# await client._send(b"S") # Accepted client version
await client._send(b"A") # Accepted client version
data = await client._recv(True)
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)
return False, client
client._key = data.decode("utf-8")
@@ -89,6 +88,10 @@ class TCPServer:
return False, 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"]:
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)
if client:
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:
case "p": # Ping packet
ev.call_event("onSentPing")
@@ -45,7 +45,6 @@ class UDPServer(asyncio.DatagramTransport):
if client._udp_sock != (self.transport, addr):
client._udp_sock = (self.transport, addr)
self.log.debug(f"Set UDP Sock for CID: {cid}")
ev.call_event("onChangePosition", data=data)
sub = data.find("{", 1)
last_pos = data[sub:]
try:
@@ -54,6 +53,7 @@ class UDPServer(asyncio.DatagramTransport):
last_pos = json.loads(last_pos)
client._last_position = last_pos
client._cars[car_id]['pos'] = last_pos
ev.call_event("onChangePosition", data, player=client, pos=last_pos)
except Exception as e:
self.log.warning(f"Cannot parse position packet: {e}")
self.log.debug(f"data: '{data}', sup: {sub}")
@@ -62,7 +62,7 @@ class UDPServer(asyncio.DatagramTransport):
case "X":
await client._send(data, to_all=True, to_self=False, to_udp=True)
case _:
self.log.warning(f" UDP [{cid}] Unknown code: {code}; {data}")
self.log.warning(f"UDP [{cid}] Unknown code: {code}; {data}")
else:
self.log.debug(f"[{cid}] Client not found.")

View File

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

View File

@@ -9,8 +9,11 @@
import asyncio
import inspect
import os
import subprocess
import sys
import types
from contextlib import contextmanager
from pathlib import Path
from threading import Thread
from core import get_logger
@@ -24,8 +27,8 @@ class KuiToi:
raise AttributeError("KuiToi: Name is required")
self.__log = get_logger(f"Plugin | {name}")
self.__name = name
self.__dir = os.path.join(self._plugins_dir, self.__name)
if not os.path.exists(self.__dir):
self.__dir = Path(self._plugins_dir) / self.__name
if not self.__dir.exists():
os.mkdir(self.__dir)
@property
@@ -42,7 +45,9 @@ 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)
path = self.__dir / file
if str(self.__dir) in str(file):
path = file
self.log.debug(f'Trying to open "{path}" with mode "{mode}"')
# Really need?
# if not os.path.exists(path):
@@ -97,6 +102,7 @@ class KuiToi:
class PluginsLoader:
_pip_dir = str(Path("pip-packets").resolve())
def __init__(self, plugins_dir):
self.loop = asyncio.get_event_loop()
@@ -110,6 +116,21 @@ class PluginsLoader:
ev.register("_plugins_get", lambda x: list(self.plugins.keys()))
console.add_command("plugins", 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):
self.log.debug("Loading plugins...")