Compare commits

..

16 Commits

Author SHA1 Message Date
b80d519c8d Update TODOs 2023-07-20 06:48:08 +03:00
2ace3fcd17 Update TODOs 2023-07-20 06:47:26 +03:00
dbe8b14d7f Add lua loading; Not supporting for now... 2023-07-20 06:44:23 +03:00
752e981462 Is this LUAAAAA? No... 2023-07-20 05:21:00 +03:00
744a7347a3 Prepare for lua support;
Minor update;
2023-07-20 04:59:12 +03:00
8139cbf8bc 0.4.0 -> 0.4.1 2023-07-20 04:10:51 +03:00
eec7c8129d Add snowman support (Only one per player);
Optimize getting car_id;
Minor fixes;
2023-07-20 04:07:39 +03:00
158599dfc5 Debug t and m codes 2023-07-20 02:22:20 +03:00
06bd50f0fa Add UDP events: onChangePosition, onSentPing 2023-07-20 01:59:30 +03:00
e086fea2e9 Update __build__; 2023-07-20 01:56:36 +03:00
b6038ee6d0 Move code from case to func;
Add events for cars: onCarDelete, onCarReset;
Minor updates;
2023-07-20 01:54:52 +03:00
147e76e089 Remove some debug info;
Optimize code.
2023-07-20 01:38:56 +03:00
56b9049dcb BeamMP_version 2023-07-20 01:37:52 +03:00
78d323644d Remove some debug data! 2023-07-20 01:34:39 +03:00
310c47162c FIX config.enc 2023-07-20 01:27:01 +03:00
27d49cf5cc Update TODOs 2023-07-20 01:15:32 +03:00
17 changed files with 361 additions and 252 deletions

View File

@ -7,7 +7,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
## TODOs
- [x] Server core
- [x] BeamMP System
- [x] BeamMP System:
- [x] Private access (Without key, Direct connect)
- [x] Public access (With key, listing in Launcher)
- [X] Player authentication
@ -19,6 +19,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [x] Chat
- [x] Players online counter
- [x] Packets handled (Recursive finding second packet)
- [ ] Client events
- [x] Car synchronizations:
- [x] State packets
- [x] Spawn cars
@ -49,14 +50,24 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
- [x] Create custom events
- [x] Return from events
- [x] Async support
- [ ] Add all events
- [x] Plugins support
- [ ] KuiToi class
- [ ] Client (Player) class
- [ ] Python part:
- [x] Load Python plugins
- [x] Async support
- [ ] Load Lua plugins (Original BeamMP compatibility)
- [ ] KuiToi class
- [ ] Client (Player) class
- [ ] JavaScript part:
- [ ] Load JavaScript plugins
- [ ] KuiToi class
- [ ] Client (Player) class
- [ ] Lua part: (Original BeamMP compatibility)
- [x] Load Lua plugins
- [ ] MP Class
- [ ] Util class
- [ ] FS class
- [x] MultiLanguage (i18n support)
- [x] Core
- [ ] Core
- [x] Console
- [x] WebAPI
- [ ] HTTP API Server (fastapi)

View File

@ -6,3 +6,4 @@ fastapi~=0.100.0
starlette~=0.27.0
pydantic~=2.0.2
click~=8.1.4
lupa~=2.0

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.tcp_server.py
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio
@ -33,8 +33,9 @@ class Client:
self.roles = None
self._guest = True
self._ready = False
self._cars = []
self._connect_time: float = 0
self._cars = [None] * 21 # Max 20 cars per player + 1 snowman
self._snowman = {"id": -1, "packet": ""}
self._connect_time = 0
@property
def _writer(self):
@ -75,13 +76,11 @@ class Client:
def is_disconnected(self):
if not self.__alive:
return True
res = self.__writer.is_closing()
if res:
self.log.debug(f"Disconnected.")
if self.__writer.is_closing():
self.log.debug(f"is_d: Disconnected.")
self.__alive = False
return True
else:
self.log.debug(f"Alive.")
self.__alive = True
return False
@ -148,7 +147,7 @@ class Client:
return
header = len(data).to_bytes(4, "little", signed=True)
self.log.debug(f'[TCP] {header + data!r}')
# self.log.debug(f'[TCP] {header + data!r}')
try:
writer.write(header + data)
await writer.drain()
@ -160,27 +159,6 @@ class Client:
await self._remove_me()
return False
# async def __handle_packet(self, data, int_header):
# self.log.debug(f"int_header: {int_header}; data: {data};")
# if len(data) != int_header:
# self.log.debug(f"WARN Expected to read {int_header} bytes, instead got {len(data)}")
#
# recv2 = data[int_header:]
# header2 = recv2[:4]
# data2 = recv2[4:]
# int_header2 = int.from_bytes(header2, byteorder='little', signed=True)
# t = asyncio.create_task(self.__handle_packet(data2, int_header2))
# self.__tasks.append(t)
# data = data[:4 + int_header]
#
# abg = b"ABG:"
# if len(data) > len(abg) and data.startswith(abg):
# data = zlib.decompress(data[len(abg):])
# self.log.debug(f"ABG Packet: {len(data)}")
#
# self.__packets_queue.append(data)
# self.log.debug(f"Packets in queue: {len(self.__packets_queue)}")
async def _recv(self, one=False):
while self.__alive:
try:
@ -196,7 +174,7 @@ class Client:
self.__packets_queue.append(None)
self.__alive = False
continue
self.log.debug(f"Header: {header}")
self.log.error(f"Header: {header}")
await self.kick("Invalid packet - header negative")
self.__packets_queue.append(None)
continue
@ -205,25 +183,20 @@ class Client:
await self.kick("Header size limit exceeded")
self.log.warning("Client sent header of >100MB - "
"assuming malicious intent and disconnecting the client.")
self.__packets_queue.append(None)
self.log.error(f"Last recv: {await self.__reader.read(100 * MB)}")
self.__packets_queue.append(None)
continue
data = await self.__reader.read(int_header)
self.log.debug(f"int_header: {int_header}; data: `{data}`;")
# self.log.debug(f"int_header: {int_header}; data: `{data}`;")
abg = b"ABG:"
if len(data) > len(abg) and data.startswith(abg):
data = zlib.decompress(data[len(abg):])
self.log.debug(f"ABG Packet: {len(data)}")
# self.log.debug(f"ABG Packet: {len(data)}")
if one:
# self.log.debug(f"int_header: `{int_header}`; data: `{data}`;")
return data
# FIXME
# else:
# t = asyncio.create_task(self.__handle_packet(data, int_header))
# self.__tasks.append(t)
self.__packets_queue.append(data)
except ConnectionError:
@ -335,8 +308,6 @@ class Client:
else:
await self._send(mod_list)
elif data == b"Done":
for c in range(int(config.Game['max_cars'] * 2.3)):
self._cars.append(None)
await self._send(f"M/levels/{config.Game['map']}/info.json")
break
return
@ -363,73 +334,84 @@ class Client:
self.log.debug(f"Invalid packet: Could not parse pid/vid from packet: '{data}'")
return -1, -1
async def _handle_car_codes(self, dta):
if len(dta) < 6:
return
sub_code = dta[1]
data = dta[3:]
match sub_code:
case "s": # Spawn car
self.log.debug("Trying to spawn car")
if data[0] == "0":
car_id = 0
for c in self.cars:
if c is None:
break
car_id += 1
self.log.debug(f"Created a car: car_id={car_id}")
async def _spawn_car(self, data):
car_data = data[2:]
car_id = next((i for i, car in enumerate(self.cars) if car is None), len(self.cars))
cars_count = len(self.cars) - self.cars.count(None)
if self._snowman['id'] != -1:
cars_count -= 1 # -1 for unicycle
self.log.debug(f"car_id={car_id}, cars_count={cars_count}")
car_json = {}
try:
car_json = json.loads(data[data.find("{"):])
car_json = json.loads(car_data[car_data.find("{"):])
except Exception as e:
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
# allow = True
allow = True
allow_snowman = True
over_spawn = False
ev_data = ev.call_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
ev_data_list = ev.call_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
d2 = await ev.call_async_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
ev_data.extend(d2)
for d in ev_data:
ev_data_list.extend(d2)
for ev_data in ev_data_list:
# TODO: handle event onCarSpawn
pass
pkt = f"Os:{self.roles}:{self.nick}:{self.cid}-{car_id}:{car_data}"
unicycle = car_json.get("jbm") == "unicycle"
# FIXME: unicycle
# if (allow and (config.Game['max_cars'] > car_id or unicycle)) or over_spawn:
if config.Game['max_cars'] > car_id and not unicycle:
self.log.debug(f"Car spawn accepted.")
snowman = car_json.get("jbm") == "unicycle"
if allow and config.Game['max_cars'] > cars_count or (snowman and allow_snowman) or over_spawn:
if snowman:
unicycle_id = self._snowman['id']
if unicycle_id != -1:
self.log.debug(f"Delete old unicycle: unicycle_id={unicycle_id}")
self._cars[unicycle_id] = None
await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True)
self._snowman = {"id": car_id, "packet": pkt}
self.log.debug(f"Unicycle spawn accepted: car_id={car_id}")
else:
self.log.debug(f"Car spawn accepted: car_id={car_id}")
self._cars[car_id] = {
"packet": pkt,
"json": car_json,
"json_ok": bool(car_json),
"unicycle": unicycle,
"over_spawn": over_spawn or unicycle
"snowman": snowman,
"over_spawn": (snowman and allow_snowman) or over_spawn
}
await self._send(pkt, to_all=True, to_self=True)
else:
await self._send(pkt)
des = f"Od:{self.cid}-{car_id}"
await self._send(des)
case "d": # Delete car
self.log.debug("Trying to delete car")
cid, car_id = self._get_cid_vid(dta)
if car_id != -1 and cid == self.cid:
# TODO: Call event onCarDelete
await self._send(dta, to_all=True, to_self=True)
try:
async def _delete_car(self, raw_data):
cid, car_id = self._get_cid_vid(raw_data)
if car_id != -1 and self.cars[car_id]:
admin_allow = False # Delete from admin, for example...
ev_data_list = ev.call_event("onCarDelete", car=self.cars[car_id], car_id=car_id, player=self)
d2 = await ev.call_async_event("onCarDelete", car=self.cars[car_id], car_id=car_id, player=self)
ev_data_list.extend(d2)
for ev_data in ev_data_list:
# TODO: handle event onCarDelete
pass
if cid == self.cid or admin_allow:
await self._send(raw_data, to_all=True, to_self=True)
car = self.cars[car_id]
if car['unicycle']:
self._cars.pop(car_id)
else:
if car['snowman']:
self.log.debug(f"Snowman found")
unicycle_id = self._snowman['id']
self._snowman['id'] = -1
self._cars[unicycle_id] = None
self._cars[car_id] = None
await self._send(f"Od:{self.cid}-{car_id}")
await self._send(f"Od:{self.cid}-{car_id}", to_all=True, to_self=True)
self.log.debug(f"Deleted car: car_id={car_id}")
except IndexError:
self.log.debug(f"Unknown car: car_id={car_id}")
case "c": # Edit car
self.log.debug("Trying to edit car")
cid, car_id = self._get_cid_vid(dta)
try:
else:
self.log.debug(f"Invalid car: car_id={car_id}")
async def _edit_car(self, raw_data, data):
cid, car_id = self._get_cid_vid(raw_data)
if car_id != -1 and self.cars[car_id]:
client = self.__Core.get_client(cid=cid)
if client:
car = client.cars[car_id]
@ -440,58 +422,73 @@ class Client:
self.log.debug(f"Invalid new_car_json: Error: {e}; Data: {data}")
allow = False
ev_data = ev.call_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
admin_allow = False
ev_data_list = ev.call_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
d2 = await ev.call_async_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
ev_data.extend(d2)
for d in ev_data:
ev_data_list.extend(d2)
for ev_data in ev_data_list:
# TODO: handle event onCarEdited
pass
if car_id != -1 and cid == self.cid or allow:
if car['unicycle']:
self._cars.pop(car_id)
await self._send(f"Od:{cid}-{car_id}", to_all=True, to_self=True)
if cid == self.cid or allow or admin_allow:
if car['snowman']:
unicycle_id = self._snowman['id']
self._snowman['id'] = -1
self.log.debug(f"Delete snowman")
await self._send(f"Od:{self.cid}-{unicycle_id}", to_all=True, to_self=True)
self._cars[unicycle_id] = None
else:
await self._send(dta, to_all=True, to_self=False)
await self._send(raw_data, to_all=True, to_self=False)
if car['json_ok']:
old_car_json = car['json']
old_car_json.update(new_car_json)
car['json'] = old_car_json
self.log.debug(f"Updated car: car_id={car_id}")
except IndexError:
self.log.debug(f"Unknown car: car_id={car_id}")
else:
self.log.debug(f"Invalid car: car_id={car_id}")
async def _reset_car(self, raw_data):
cid, car_id = self._get_cid_vid(raw_data)
if car_id != -1 and cid == self.cid and self.cars[car_id]:
await self._send(raw_data, to_all=True, to_self=False)
ev.call_event("onCarReset", car=self.cars[car_id], car_id=car_id, player=self)
await ev.call_async_event("onCarReset", car=self.cars[car_id], car_id=car_id, player=self)
self.log.debug(f"Car reset: car_id={car_id}")
else:
self.log.debug(f"Invalid car: car_id={car_id}")
async def _handle_car_codes(self, raw_data):
if len(raw_data) < 6:
return
sub_code = raw_data[1]
data = raw_data[3:]
match sub_code:
case "s": # Spawn car
self.log.debug("Trying to spawn car")
if data[0] == "0":
await self._spawn_car(data)
case "d": # Delete car
self.log.debug("Trying to delete car")
await self._delete_car(raw_data)
case "c": # Edit car
self.log.debug("Trying to edit car")
await self._edit_car(raw_data, data)
case "r": # Reset car
self.log.debug("Trying to reset car")
cid, car_id = self._get_cid_vid(dta)
if car_id != -1 and cid == self.cid:
# TODO: Call event onCarReset
await self._send(dta, to_all=True, to_self=False)
self.log.debug(f"Car reset: car_id={car_id}")
case "t":
self.log.debug(f"Received 'Ot' packet: {dta}")
await self._send(dta, to_all=True, to_self=False)
case "m":
await self._send(dta, to_all=True, to_self=True)
await self._reset_car(raw_data)
async def _handle_codes(self, data):
if not data:
self.__alive = False
return
case "t": # Broken details
self.log.debug(f"Something changed/broken: {raw_data}")
await self._send(raw_data, to_all=True, to_self=False)
# Codes: V W X Y
if 89 >= data[0] >= 86:
await self._send(data, to_all=True, to_self=False)
try:
data = data.decode()
except UnicodeDecodeError:
self.log.debug(f"UnicodeDecodeError: {data}")
return
case "m": # Move focus cat
self.log.debug(f"Move focus to: {raw_data}")
await self._send(raw_data, to_all=True, to_self=True)
code = data[0]
# Codes: p, Z in udp_server.py
match code:
case "H":
async def _connected_handler(self):
self.log.info(f"Syncing time: {round(time.monotonic() - self._connect_time, 2)}s")
# Client connected
ev.call_event("onPlayerJoin", player=self)
@ -509,7 +506,7 @@ class Client:
continue
await self._send(car['packet'])
case "C": # Chat handler
async def _chat_handler(self, data):
sup = data.find(":", 2)
if sup == -1:
await self._send("C:Server: Invalid message.")
@ -517,7 +514,6 @@ class Client:
if not msg:
self.log.debug("Tried to send an empty event, ignoring")
return
self.log.info(f"{self.nick}: {msg}")
to_ev = {"message": msg, "player": self}
ev_data_list = ev.call_event("onChatReceive", **to_ev)
d2 = await ev.call_async_event("onChatReceive", **to_ev)
@ -537,18 +533,44 @@ class Client:
if to_client:
# noinspection PyProtectedMember
writer = to_client._writer
self.log.info(f"{message}" if to_all else f"{self.nick}: {msg}")
await self._send(f"C:{message}", to_all=to_all, to_self=to_self, writer=writer)
need_send = False
except KeyError | AttributeError:
self.log.error(f"Returns invalid data: {ev_data}")
if need_send:
self.log.info(f"{self.nick}: {msg}")
await self._send(data, to_all=True)
async def _handle_codes(self, data):
if not data:
self.__alive = False
return
# Codes: V W X Y
if 89 >= data[0] >= 86:
await self._send(data, to_all=True, to_self=False)
return
try:
data = data.decode()
except UnicodeDecodeError:
self.log.error(f"UnicodeDecodeError: {data}")
return
# Codes: p, Z in udp_server.py
match data[0]: # At data[0] code
case "H": # Map load, client ready
await self._connected_handler()
case "C": # Chat handler
await self._chat_handler(data)
case "O": # Cars handler
await self._handle_car_codes(data)
case "E": # Client events handler
# TODO: HandleEvent
# TODO: Handle events from client
pass
case "N":
@ -564,7 +586,7 @@ class Client:
while self.__alive:
if len(self.__packets_queue) > 0:
for index, packet in enumerate(self.__packets_queue):
self.log.debug(f"Packet: {packet}")
# self.log.debug(f"Packet: {packet}")
del self.__packets_queue[index]
task = self._loop.create_task(self._handle_codes(packet))
tasks.append(task)

View File

@ -1,13 +1,13 @@
# Developed by KuiToi Dev
# File core.tcp_server.py
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio
from asyncio import StreamReader, StreamWriter, DatagramTransport
from logging import Logger
from typing import Tuple, List, Dict
from typing import Tuple, List, Dict, Optional, Union
from core import Core, utils
@ -15,7 +15,7 @@ from core import Core, utils
class Client:
def __init__(self, reader: StreamReader, writer: StreamWriter, core: Core) -> "Client":
self._connect_time: float = None
self._connect_time: float = 0.0
self.__tasks = []
self.__reader = reader
self.__writer = writer
@ -33,7 +33,9 @@ class Client:
self._guest = True
self.__alive = True
self._ready = False
self._cars: List[dict | None] = []
self._cars: List[Optional[Dict[str, int]]] = []
self._snowman: Dict[str, Union[int, str]] = {"id": -1, "packet": ""}
@property
def _writer(self) -> StreamWriter: ...
@property
@ -56,12 +58,17 @@ class Client:
async def send_event(self, event_name: str, event_data: str) -> None: ...
async def _send(self, data: bytes | str, to_all: bool = False, to_self: bool = True, to_udp: bool = False, writer: StreamWriter = None) -> None: ...
async def _sync_resources(self) -> None: ...
# async def __handle_packet(self, data, int_header): ...
async def _recv(self, one=False) -> bytes | None: ...
async def _split_load(self, start: int, end: int, d_sock: bool, filename: str, sl: float) -> None: ...
async def _get_cid_vid(self, s: str) -> Tuple[int, int]: ...
async def _handle_car_codes(self, data) -> None: ...
async def _handle_codes(self, data) -> None: ...
async def _spawn_car(self, data: str) -> None: ...
async def _delete_car(self, raw_data: str) -> None: ...
async def _edit_car(self, raw_data: str, data: str) -> None: ...
async def _reset_car(self, raw_data: str) -> None: ...
async def _handle_car_codes(self, data: str) -> None: ...
async def _connected_handler(self) -> None: ...
async def _chat_handler(self, data: str) -> None: ...
async def _handle_codes(self, data: bytes) -> None: ...
async def _looper(self) -> None: ...
def _update_logger(self) -> None: ...
async def _remove_me(self) -> None: ...

View File

@ -2,7 +2,7 @@
# File core.__init__.py
# Written by: SantaSpeen
# Version 1.3
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
# Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai)
@ -10,8 +10,8 @@
__title__ = 'KuiToi-Server'
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
__version__ = '0.4.0'
__build__ = 1361 # Я это считаю лог файлами
__version__ = '0.4.1'
__build__ = 1486 # Я это считаю лог файлами
__author__ = 'SantaSpeen'
__author_email__ = 'admin@kuitoi.su'
__license__ = "FPA"
@ -46,13 +46,13 @@ if args.config:
config_provider = ConfigProvider(config_path)
config = config_provider.open_config()
builtins.config = config
config.enc = config.Options['encoding']
if config.Options['debug'] is True:
utils.set_debug_status()
log.info("Debug enabled!")
log = get_logger("core.init")
log.debug("Debug mode enabled!")
log.debug(f"Server config: {config}")
config.enc = config.Options['encoding']
# i18n init
log.debug("Initializing i18n...")
ml = MultiLanguage()

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.core.py
# Written by: SantaSpeen
# Version: 0.4.0
# Version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio
@ -16,7 +16,7 @@ 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 import PluginsLoader, LuaPluginsLoader
from modules.WebAPISystem import app as webapp
@ -44,7 +44,7 @@ class Core:
self.lock_upload = False
self.client_major_version = "2.0"
self.BeamMP_version = "3.2.0"
self.BeamMP_version = "3.1.1" # 20.07.2023
ev.register_event("get_player", self.get_client)
@ -217,11 +217,14 @@ class Core:
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")
pl_dir = "plugins"
self.log.debug("Initializing PluginsLoaders...")
if not os.path.exists(pl_dir):
os.mkdir(pl_dir)
pl = PluginsLoader(pl_dir)
await pl.load()
lpl = LuaPluginsLoader(pl_dir)
await lpl.load()
try:
# WebApi Start
@ -263,6 +266,7 @@ class Core:
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
await ev.call_async_event("_plugins_start")
await ev.call_async_event("_lua_plugins_start")
self.run = True
self.log.info(i18n.start)
@ -287,6 +291,7 @@ class Core:
ev.call_event("onServerStopped")
await ev.call_async_event("onServerStopped")
await ev.call_async_event("_plugins_unload")
await ev.call_async_event("_lua_plugins_unload")
self.run = False
self.log.info(i18n.stop)
if config.WebAPI["enabled"]:

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.core.pyi
# Written by: SantaSpeen
# Version 0.4.0
# Version 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.tcp_server.py
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.tcp_server.pyi
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.udp_server.py
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio
@ -9,6 +9,7 @@ import asyncio
from core import utils
# noinspection PyProtectedMember
class UDPServer(asyncio.DatagramTransport):
transport = None
@ -20,11 +21,8 @@ class UDPServer(asyncio.DatagramTransport):
self.host = host
self.port = port
self.run = False
# self.transport = transport
def connection_made(self, transport):
self.log.debug("set connection transport")
# self.transport = self.transport()
def connection_made(self, transport): ...
async def handle_datagram(self, data, addr):
try:
@ -33,16 +31,15 @@ class UDPServer(asyncio.DatagramTransport):
client = self.Core.get_client(cid=cid)
if client:
match code:
case "p": # Ping packet
ev.call_event("onSentPing")
self.transport.sendto(b"p", addr)
case "Z": # Position packet
if client._udp_sock != (self.transport, addr):
client._udp_sock = (self.transport, addr)
self.log.debug(f"Set UDP Sock for CID: {cid}")
match code:
case "p":
self.log.debug(f"[{cid}] Send ping")
# TODO: Call event onSentPing
self.transport.sendto(b"p", addr) # Send ping
case "Z":
# TODO: Call event onChangePosition
ev.call_event("onChangePosition")
if client:
await client._send(data[2:], to_all=True, to_self=False, to_udp=True)
case _:
@ -86,7 +83,6 @@ class UDPServer(asyncio.DatagramTransport):
self.run = True
while not self.transport.is_closing():
await asyncio.sleep(0.2)
self.log.info("UDP сервер сдох 1")
except OSError as e:
self.log.error("Cannot bind port or other error")
self.log.exception(e)

View File

@ -1,7 +1,7 @@
# Developed by KuiToi Dev
# File core.udp_server.py
# Written by: SantaSpeen
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import asyncio

View File

@ -2,7 +2,7 @@
# File core.utils.py
# Written by: SantaSpeen
# Version 1.1
# Core version: 0.4.0
# Core version: 0.4.1
# Licence: FPA
# (c) kuitoi.su 2023
import datetime

View File

@ -28,7 +28,11 @@ class EventsSystem:
"onPlayerJoin": [],
"onChatReceive": [],
"onCarSpawn": [],
"onCarDelete": [],
"onCarEdited": [],
"onCarReset": [],
"onSentPing": [], # Only sync
"onChangePosition": [], # Only sync
"onServerStopped": [],
}
self.__async_events = {
@ -36,7 +40,9 @@ class EventsSystem:
"onPlayerJoin": [],
"onChatReceive": [],
"onCarSpawn": [],
"onCarDelete": [],
"onCarEdited": [],
"onCarReset": [],
"onServerStopped": []
}
@ -82,6 +88,7 @@ class EventsSystem:
return funcs_data
def call_event(self, event_name, *args, **kwargs):
if event_name not in ["onChangePosition", "onSentPing"]: # UDP events
self.log.debug(f"Calling sync event: '{event_name}'")
funcs_data = []

View File

@ -1 +1,2 @@
from .plugins_loader import PluginsLoader
from .lua_plugins_loader import LuaPluginsLoader

View File

@ -0,0 +1,67 @@
import asyncio
import os
# noinspection PyUnresolvedReferences
import lupa.lua53
from lupa.lua53 import LuaRuntime
from core import get_logger
class MP:
def __init__(self, name):
self.name = name
self.log = get_logger(f"LuaPlugin | {name}")
def log_info(self, *args):
s = ""
for i in args:
s += i
self.log.info(s)
class LuaPluginsLoader:
def __init__(self, plugins_dir):
self.loop = asyncio.get_event_loop()
self.plugins_dir = plugins_dir
self.lua_plugins = {}
self.lua_plugins_tasks = []
self.lua_dirs = []
self.log = get_logger("LuaPluginsLoader")
self.loaded_str = "Lua plugins: "
ev.register_event("_lua_plugins_start", self.start)
ev.register_event("_lua_plugins_unload", self.unload)
console.add_command("lua_plugins", lambda x: self.loaded_str[:-2])
console.add_command("lua_pl", lambda x: self.loaded_str[:-2])
async def load(self):
self.log.debug("Loading Lua plugins...")
py_folders = ev.call_event("_plugins_get")[0]
for obj in os.listdir(self.plugins_dir):
path = os.path.join(self.plugins_dir, obj)
if os.path.isdir(path) and obj not in py_folders and obj not in "__pycache__":
if os.path.isfile(os.path.join(path, "main.lua")):
self.lua_dirs.append([path, obj])
self.log.debug(f"py_folders {py_folders}, lua_dirs {self.lua_dirs}")
for path, obj in self.lua_dirs:
lua = LuaRuntime(encoding=config.enc, source_encoding=config.enc)
mp = MP(obj)
lua.globals().MP = mp
lua.globals().print = mp.log_info
code = f'package.path = package.path.."' \
f';{self.plugins_dir}/{obj}/?.lua' \
f';{self.plugins_dir}/{obj}/lua/?.lua' \
f';modules/PluginsLoader/lua_libs/?.lua"'
with open(os.path.join(path, "main.lua"), 'r', encoding=config.enc) as f:
code += f.read()
lua.execute(code)
async def start(self, _):
...
async def unload(self, _):
...

View File

@ -33,20 +33,10 @@ class KuiToi:
def name(self):
return self.__name
@name.setter
def name(self, value):
# You chell not pass
pass
@property
def dir(self):
return self.__dir
@dir.setter
def dir(self, value):
# You chell not pass
pass
@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)
@ -83,6 +73,7 @@ class PluginsLoader:
self.loaded_str = "Plugins: "
ev.register_event("_plugins_start", self.start)
ev.register_event("_plugins_unload", self.unload)
ev.register_event("_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])
@ -90,14 +81,14 @@ class PluginsLoader:
self.log.debug("Loading plugins...")
files = os.listdir(self.plugins_dir)
for file in files:
if file.endswith(".py"):
file_path = os.path.join(self.plugins_dir, file)
if os.path.isfile(file_path) and file.endswith(".py"):
try:
self.log.debug(f"Loading plugin: {file[:-3]}")
plugin = types.ModuleType(file[:-3])
plugin.KuiToi = KuiToi
plugin.KuiToi._plugins_dir = self.plugins_dir
plugin.print = print
file_path = os.path.join(self.plugins_dir, file)
plugin.__file__ = file_path
with open(f'{file_path}', 'r', encoding=config.enc) as f:
code = f.read()

View File

@ -11,5 +11,6 @@ from .ConfigProvider import ConfigProvider, Config
from .i18n import MultiLanguage
from .EventsSystem import EventsSystem
from .PluginsLoader import PluginsLoader
from .PluginsLoader import LuaPluginsLoader
from .WebAPISystem import web_app
from .WebAPISystem import _stop as stop_web