mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 16:25:36 +00:00
Compare commits
16 Commits
a5a7a5dfc9
...
b80d519c8d
Author | SHA1 | Date | |
---|---|---|---|
b80d519c8d | |||
2ace3fcd17 | |||
dbe8b14d7f | |||
752e981462 | |||
744a7347a3 | |||
8139cbf8bc | |||
eec7c8129d | |||
158599dfc5 | |||
06bd50f0fa | |||
e086fea2e9 | |||
b6038ee6d0 | |||
147e76e089 | |||
56b9049dcb | |||
78d323644d | |||
310c47162c | |||
27d49cf5cc |
25
README.md
25
README.md
@ -7,7 +7,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
## TODOs
|
## TODOs
|
||||||
|
|
||||||
- [x] Server core
|
- [x] Server core
|
||||||
- [x] BeamMP System
|
- [x] BeamMP System:
|
||||||
- [x] Private access (Without key, Direct connect)
|
- [x] Private access (Without key, Direct connect)
|
||||||
- [x] Public access (With key, listing in Launcher)
|
- [x] Public access (With key, listing in Launcher)
|
||||||
- [X] Player authentication
|
- [X] Player authentication
|
||||||
@ -19,6 +19,7 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [x] Chat
|
- [x] Chat
|
||||||
- [x] Players online counter
|
- [x] Players online counter
|
||||||
- [x] Packets handled (Recursive finding second packet)
|
- [x] Packets handled (Recursive finding second packet)
|
||||||
|
- [ ] Client events
|
||||||
- [x] Car synchronizations:
|
- [x] Car synchronizations:
|
||||||
- [x] State packets
|
- [x] State packets
|
||||||
- [x] Spawn cars
|
- [x] Spawn cars
|
||||||
@ -49,14 +50,24 @@ BeamingDrive Multiplayer (BeamMP) server compatible with BeamMP clients.
|
|||||||
- [x] Create custom events
|
- [x] Create custom events
|
||||||
- [x] Return from events
|
- [x] Return from events
|
||||||
- [x] Async support
|
- [x] Async support
|
||||||
|
- [ ] Add all events
|
||||||
- [x] Plugins support
|
- [x] Plugins support
|
||||||
- [ ] KuiToi class
|
- [ ] Python part:
|
||||||
- [ ] Client (Player) class
|
- [x] Load Python plugins
|
||||||
- [x] Load Python plugins
|
- [x] Async support
|
||||||
- [x] Async support
|
- [ ] KuiToi class
|
||||||
- [ ] Load Lua plugins (Original BeamMP compatibility)
|
- [ ] 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] MultiLanguage (i18n support)
|
||||||
- [x] Core
|
- [ ] Core
|
||||||
- [x] Console
|
- [x] Console
|
||||||
- [x] WebAPI
|
- [x] WebAPI
|
||||||
- [ ] HTTP API Server (fastapi)
|
- [ ] HTTP API Server (fastapi)
|
||||||
|
@ -5,4 +5,5 @@ uvicorn~=0.22.0
|
|||||||
fastapi~=0.100.0
|
fastapi~=0.100.0
|
||||||
starlette~=0.27.0
|
starlette~=0.27.0
|
||||||
pydantic~=2.0.2
|
pydantic~=2.0.2
|
||||||
click~=8.1.4
|
click~=8.1.4
|
||||||
|
lupa~=2.0
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.tcp_server.py
|
# File core.tcp_server.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -33,8 +33,9 @@ class Client:
|
|||||||
self.roles = None
|
self.roles = None
|
||||||
self._guest = True
|
self._guest = True
|
||||||
self._ready = False
|
self._ready = False
|
||||||
self._cars = []
|
self._cars = [None] * 21 # Max 20 cars per player + 1 snowman
|
||||||
self._connect_time: float = 0
|
self._snowman = {"id": -1, "packet": ""}
|
||||||
|
self._connect_time = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _writer(self):
|
def _writer(self):
|
||||||
@ -75,13 +76,11 @@ class Client:
|
|||||||
def is_disconnected(self):
|
def is_disconnected(self):
|
||||||
if not self.__alive:
|
if not self.__alive:
|
||||||
return True
|
return True
|
||||||
res = self.__writer.is_closing()
|
if self.__writer.is_closing():
|
||||||
if res:
|
self.log.debug(f"is_d: Disconnected.")
|
||||||
self.log.debug(f"Disconnected.")
|
|
||||||
self.__alive = False
|
self.__alive = False
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.log.debug(f"Alive.")
|
|
||||||
self.__alive = True
|
self.__alive = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -148,7 +147,7 @@ class Client:
|
|||||||
return
|
return
|
||||||
|
|
||||||
header = len(data).to_bytes(4, "little", signed=True)
|
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:
|
try:
|
||||||
writer.write(header + data)
|
writer.write(header + data)
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
@ -160,27 +159,6 @@ class Client:
|
|||||||
await self._remove_me()
|
await self._remove_me()
|
||||||
return False
|
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):
|
async def _recv(self, one=False):
|
||||||
while self.__alive:
|
while self.__alive:
|
||||||
try:
|
try:
|
||||||
@ -196,7 +174,7 @@ class Client:
|
|||||||
self.__packets_queue.append(None)
|
self.__packets_queue.append(None)
|
||||||
self.__alive = False
|
self.__alive = False
|
||||||
continue
|
continue
|
||||||
self.log.debug(f"Header: {header}")
|
self.log.error(f"Header: {header}")
|
||||||
await self.kick("Invalid packet - header negative")
|
await self.kick("Invalid packet - header negative")
|
||||||
self.__packets_queue.append(None)
|
self.__packets_queue.append(None)
|
||||||
continue
|
continue
|
||||||
@ -205,25 +183,20 @@ class Client:
|
|||||||
await self.kick("Header size limit exceeded")
|
await self.kick("Header size limit exceeded")
|
||||||
self.log.warning("Client sent header of >100MB - "
|
self.log.warning("Client sent header of >100MB - "
|
||||||
"assuming malicious intent and disconnecting the client.")
|
"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.log.error(f"Last recv: {await self.__reader.read(100 * MB)}")
|
||||||
|
self.__packets_queue.append(None)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
data = await self.__reader.read(int_header)
|
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:"
|
abg = b"ABG:"
|
||||||
if len(data) > len(abg) and data.startswith(abg):
|
if len(data) > len(abg) and data.startswith(abg):
|
||||||
data = zlib.decompress(data[len(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:
|
if one:
|
||||||
# self.log.debug(f"int_header: `{int_header}`; data: `{data}`;")
|
|
||||||
return data
|
return data
|
||||||
# FIXME
|
|
||||||
# else:
|
|
||||||
# t = asyncio.create_task(self.__handle_packet(data, int_header))
|
|
||||||
# self.__tasks.append(t)
|
|
||||||
self.__packets_queue.append(data)
|
self.__packets_queue.append(data)
|
||||||
|
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
@ -335,8 +308,6 @@ class Client:
|
|||||||
else:
|
else:
|
||||||
await self._send(mod_list)
|
await self._send(mod_list)
|
||||||
elif data == b"Done":
|
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")
|
await self._send(f"M/levels/{config.Game['map']}/info.json")
|
||||||
break
|
break
|
||||||
return
|
return
|
||||||
@ -363,116 +334,213 @@ class Client:
|
|||||||
self.log.debug(f"Invalid packet: Could not parse pid/vid from packet: '{data}'")
|
self.log.debug(f"Invalid packet: Could not parse pid/vid from packet: '{data}'")
|
||||||
return -1, -1
|
return -1, -1
|
||||||
|
|
||||||
async def _handle_car_codes(self, dta):
|
async def _spawn_car(self, data):
|
||||||
if len(dta) < 6:
|
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(car_data[car_data.find("{"):])
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
|
||||||
|
allow = True
|
||||||
|
allow_snowman = True
|
||||||
|
over_spawn = False
|
||||||
|
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_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}"
|
||||||
|
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),
|
||||||
|
"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)
|
||||||
|
|
||||||
|
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['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}", to_all=True, to_self=True)
|
||||||
|
self.log.debug(f"Deleted car: car_id={car_id}")
|
||||||
|
|
||||||
|
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]
|
||||||
|
new_car_json = {}
|
||||||
|
try:
|
||||||
|
new_car_json = json.loads(data[data.find("{"):])
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug(f"Invalid new_car_json: Error: {e}; Data: {data}")
|
||||||
|
|
||||||
|
allow = False
|
||||||
|
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_list.extend(d2)
|
||||||
|
for ev_data in ev_data_list:
|
||||||
|
# TODO: handle event onCarEdited
|
||||||
|
pass
|
||||||
|
|
||||||
|
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(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}")
|
||||||
|
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
|
return
|
||||||
sub_code = dta[1]
|
sub_code = raw_data[1]
|
||||||
data = dta[3:]
|
data = raw_data[3:]
|
||||||
match sub_code:
|
match sub_code:
|
||||||
case "s": # Spawn car
|
case "s": # Spawn car
|
||||||
self.log.debug("Trying to spawn car")
|
self.log.debug("Trying to spawn car")
|
||||||
if data[0] == "0":
|
if data[0] == "0":
|
||||||
car_id = 0
|
await self._spawn_car(data)
|
||||||
for c in self.cars:
|
|
||||||
if c is None:
|
|
||||||
break
|
|
||||||
car_id += 1
|
|
||||||
self.log.debug(f"Created a car: car_id={car_id}")
|
|
||||||
car_data = data[2:]
|
|
||||||
car_json = {}
|
|
||||||
try:
|
|
||||||
car_json = json.loads(data[data.find("{"):])
|
|
||||||
except Exception as e:
|
|
||||||
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
|
|
||||||
# allow = True
|
|
||||||
over_spawn = False
|
|
||||||
ev_data = 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:
|
|
||||||
# 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.")
|
|
||||||
self._cars[car_id] = {
|
|
||||||
"packet": pkt,
|
|
||||||
"json": car_json,
|
|
||||||
"json_ok": bool(car_json),
|
|
||||||
"unicycle": unicycle,
|
|
||||||
"over_spawn": over_spawn or unicycle
|
|
||||||
}
|
|
||||||
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
|
case "d": # Delete car
|
||||||
self.log.debug("Trying to delete car")
|
self.log.debug("Trying to delete car")
|
||||||
cid, car_id = self._get_cid_vid(dta)
|
await self._delete_car(raw_data)
|
||||||
if car_id != -1 and cid == self.cid:
|
|
||||||
# TODO: Call event onCarDelete
|
|
||||||
await self._send(dta, to_all=True, to_self=True)
|
|
||||||
try:
|
|
||||||
car = self.cars[car_id]
|
|
||||||
if car['unicycle']:
|
|
||||||
self._cars.pop(car_id)
|
|
||||||
else:
|
|
||||||
self._cars[car_id] = None
|
|
||||||
await self._send(f"Od:{self.cid}-{car_id}")
|
|
||||||
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
|
case "c": # Edit car
|
||||||
self.log.debug("Trying to edit car")
|
self.log.debug("Trying to edit car")
|
||||||
cid, car_id = self._get_cid_vid(dta)
|
await self._edit_car(raw_data, data)
|
||||||
try:
|
|
||||||
client = self.__Core.get_client(cid=cid)
|
|
||||||
if client:
|
|
||||||
car = client.cars[car_id]
|
|
||||||
new_car_json = {}
|
|
||||||
try:
|
|
||||||
new_car_json = json.loads(data[data.find("{"):])
|
|
||||||
except Exception as e:
|
|
||||||
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)
|
|
||||||
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:
|
|
||||||
# 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)
|
|
||||||
else:
|
|
||||||
await self._send(dta, 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}")
|
|
||||||
|
|
||||||
case "r": # Reset car
|
case "r": # Reset car
|
||||||
self.log.debug("Trying to reset car")
|
self.log.debug("Trying to reset car")
|
||||||
cid, car_id = self._get_cid_vid(dta)
|
await self._reset_car(raw_data)
|
||||||
if car_id != -1 and cid == self.cid:
|
|
||||||
# TODO: Call event onCarReset
|
case "t": # Broken details
|
||||||
await self._send(dta, to_all=True, to_self=False)
|
self.log.debug(f"Something changed/broken: {raw_data}")
|
||||||
self.log.debug(f"Car reset: car_id={car_id}")
|
await self._send(raw_data, to_all=True, to_self=False)
|
||||||
case "t":
|
|
||||||
self.log.debug(f"Received 'Ot' packet: {dta}")
|
case "m": # Move focus cat
|
||||||
await self._send(dta, to_all=True, to_self=False)
|
self.log.debug(f"Move focus to: {raw_data}")
|
||||||
case "m":
|
await self._send(raw_data, to_all=True, to_self=True)
|
||||||
await self._send(dta, to_all=True, to_self=True)
|
|
||||||
|
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)
|
||||||
|
await ev.call_async_event("onPlayerJoin", player=self)
|
||||||
|
|
||||||
|
await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it
|
||||||
|
await self._send(f"JWelcome {self.nick}!", to_all=True) # Hello message
|
||||||
|
self._ready = True
|
||||||
|
|
||||||
|
for client in self.__Core.clients:
|
||||||
|
if not client:
|
||||||
|
continue
|
||||||
|
for car in client.cars:
|
||||||
|
if not car:
|
||||||
|
continue
|
||||||
|
await self._send(car['packet'])
|
||||||
|
|
||||||
|
async def _chat_handler(self, data):
|
||||||
|
sup = data.find(":", 2)
|
||||||
|
if sup == -1:
|
||||||
|
await self._send("C:Server: Invalid message.")
|
||||||
|
msg = data[sup + 2:]
|
||||||
|
if not msg:
|
||||||
|
self.log.debug("Tried to send an empty event, ignoring")
|
||||||
|
return
|
||||||
|
to_ev = {"message": msg, "player": self}
|
||||||
|
ev_data_list = ev.call_event("onChatReceive", **to_ev)
|
||||||
|
d2 = await ev.call_async_event("onChatReceive", **to_ev)
|
||||||
|
ev_data_list.extend(d2)
|
||||||
|
need_send = True
|
||||||
|
for ev_data in ev_data_list:
|
||||||
|
try:
|
||||||
|
message = ev_data["message"]
|
||||||
|
to_all = ev_data.get("to_all")
|
||||||
|
if to_all is None:
|
||||||
|
to_all = True
|
||||||
|
to_self = ev_data.get("to_self")
|
||||||
|
if to_self is None:
|
||||||
|
to_self = True
|
||||||
|
to_client = ev_data.get("to_client")
|
||||||
|
writer = None
|
||||||
|
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):
|
async def _handle_codes(self, data):
|
||||||
if not data:
|
if not data:
|
||||||
@ -482,73 +550,27 @@ class Client:
|
|||||||
# Codes: V W X Y
|
# Codes: V W X Y
|
||||||
if 89 >= data[0] >= 86:
|
if 89 >= data[0] >= 86:
|
||||||
await self._send(data, to_all=True, to_self=False)
|
await self._send(data, to_all=True, to_self=False)
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = data.decode()
|
data = data.decode()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
self.log.debug(f"UnicodeDecodeError: {data}")
|
self.log.error(f"UnicodeDecodeError: {data}")
|
||||||
return
|
return
|
||||||
|
|
||||||
code = data[0]
|
|
||||||
# Codes: p, Z in udp_server.py
|
# Codes: p, Z in udp_server.py
|
||||||
match code:
|
match data[0]: # At data[0] code
|
||||||
case "H":
|
case "H": # Map load, client ready
|
||||||
self.log.info(f"Syncing time: {round(time.monotonic() - self._connect_time, 2)}s")
|
await self._connected_handler()
|
||||||
# Client connected
|
|
||||||
ev.call_event("onPlayerJoin", player=self)
|
|
||||||
await ev.call_async_event("onPlayerJoin", player=self)
|
|
||||||
|
|
||||||
await self._send(f"Sn{self.nick}", to_all=True) # I don't know for what it
|
|
||||||
await self._send(f"JWelcome {self.nick}!", to_all=True) # Hello message
|
|
||||||
self._ready = True
|
|
||||||
|
|
||||||
for client in self.__Core.clients:
|
|
||||||
if not client:
|
|
||||||
continue
|
|
||||||
for car in client.cars:
|
|
||||||
if not car:
|
|
||||||
continue
|
|
||||||
await self._send(car['packet'])
|
|
||||||
|
|
||||||
case "C": # Chat handler
|
case "C": # Chat handler
|
||||||
sup = data.find(":", 2)
|
await self._chat_handler(data)
|
||||||
if sup == -1:
|
|
||||||
await self._send("C:Server: Invalid message.")
|
|
||||||
msg = data[sup + 2:]
|
|
||||||
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)
|
|
||||||
ev_data_list.extend(d2)
|
|
||||||
need_send = True
|
|
||||||
for ev_data in ev_data_list:
|
|
||||||
try:
|
|
||||||
message = ev_data["message"]
|
|
||||||
to_all = ev_data.get("to_all")
|
|
||||||
if to_all is None:
|
|
||||||
to_all = True
|
|
||||||
to_self = ev_data.get("to_self")
|
|
||||||
if to_self is None:
|
|
||||||
to_self = True
|
|
||||||
to_client = ev_data.get("to_client")
|
|
||||||
writer = None
|
|
||||||
if to_client:
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
writer = to_client._writer
|
|
||||||
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:
|
|
||||||
await self._send(data, to_all=True)
|
|
||||||
|
|
||||||
case "O": # Cars handler
|
case "O": # Cars handler
|
||||||
await self._handle_car_codes(data)
|
await self._handle_car_codes(data)
|
||||||
|
|
||||||
case "E": # Client events handler
|
case "E": # Client events handler
|
||||||
# TODO: HandleEvent
|
# TODO: Handle events from client
|
||||||
pass
|
pass
|
||||||
|
|
||||||
case "N":
|
case "N":
|
||||||
@ -564,7 +586,7 @@ class Client:
|
|||||||
while self.__alive:
|
while self.__alive:
|
||||||
if len(self.__packets_queue) > 0:
|
if len(self.__packets_queue) > 0:
|
||||||
for index, packet in enumerate(self.__packets_queue):
|
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]
|
del self.__packets_queue[index]
|
||||||
task = self._loop.create_task(self._handle_codes(packet))
|
task = self._loop.create_task(self._handle_codes(packet))
|
||||||
tasks.append(task)
|
tasks.append(task)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.tcp_server.py
|
# File core.tcp_server.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
from asyncio import StreamReader, StreamWriter, DatagramTransport
|
from asyncio import StreamReader, StreamWriter, DatagramTransport
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
from typing import Tuple, List, Dict
|
from typing import Tuple, List, Dict, Optional, Union
|
||||||
|
|
||||||
from core import Core, utils
|
from core import Core, utils
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ from core import Core, utils
|
|||||||
class Client:
|
class Client:
|
||||||
|
|
||||||
def __init__(self, reader: StreamReader, writer: StreamWriter, core: Core) -> "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.__tasks = []
|
||||||
self.__reader = reader
|
self.__reader = reader
|
||||||
self.__writer = writer
|
self.__writer = writer
|
||||||
@ -33,7 +33,9 @@ class Client:
|
|||||||
self._guest = True
|
self._guest = True
|
||||||
self.__alive = True
|
self.__alive = True
|
||||||
self._ready = False
|
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
|
@property
|
||||||
def _writer(self) -> StreamWriter: ...
|
def _writer(self) -> StreamWriter: ...
|
||||||
@property
|
@property
|
||||||
@ -56,12 +58,17 @@ class Client:
|
|||||||
async def send_event(self, event_name: str, event_data: str) -> None: ...
|
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 _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 _sync_resources(self) -> None: ...
|
||||||
# async def __handle_packet(self, data, int_header): ...
|
|
||||||
async def _recv(self, one=False) -> bytes | None: ...
|
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 _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 _get_cid_vid(self, s: str) -> Tuple[int, int]: ...
|
||||||
async def _handle_car_codes(self, data) -> None: ...
|
async def _spawn_car(self, data: str) -> None: ...
|
||||||
async def _handle_codes(self, data) -> 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: ...
|
async def _looper(self) -> None: ...
|
||||||
def _update_logger(self) -> None: ...
|
def _update_logger(self) -> None: ...
|
||||||
async def _remove_me(self) -> None: ...
|
async def _remove_me(self) -> None: ...
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# File core.__init__.py
|
# File core.__init__.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Version 1.3
|
# Version 1.3
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
# Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai)
|
# Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai)
|
||||||
@ -10,8 +10,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.0'
|
__version__ = '0.4.1'
|
||||||
__build__ = 1361 # Я это считаю лог файлами
|
__build__ = 1486 # Я это считаю лог файлами
|
||||||
__author__ = 'SantaSpeen'
|
__author__ = 'SantaSpeen'
|
||||||
__author_email__ = 'admin@kuitoi.su'
|
__author_email__ = 'admin@kuitoi.su'
|
||||||
__license__ = "FPA"
|
__license__ = "FPA"
|
||||||
@ -46,13 +46,13 @@ if args.config:
|
|||||||
config_provider = ConfigProvider(config_path)
|
config_provider = ConfigProvider(config_path)
|
||||||
config = config_provider.open_config()
|
config = config_provider.open_config()
|
||||||
builtins.config = config
|
builtins.config = config
|
||||||
|
config.enc = config.Options['encoding']
|
||||||
if config.Options['debug'] is True:
|
if config.Options['debug'] is True:
|
||||||
utils.set_debug_status()
|
utils.set_debug_status()
|
||||||
log.info("Debug enabled!")
|
log.info("Debug enabled!")
|
||||||
log = get_logger("core.init")
|
log = get_logger("core.init")
|
||||||
log.debug("Debug mode enabled!")
|
log.debug("Debug mode enabled!")
|
||||||
log.debug(f"Server config: {config}")
|
log.debug(f"Server config: {config}")
|
||||||
config.enc = config.Options['encoding']
|
|
||||||
# i18n init
|
# i18n init
|
||||||
log.debug("Initializing i18n...")
|
log.debug("Initializing i18n...")
|
||||||
ml = MultiLanguage()
|
ml = MultiLanguage()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.core.py
|
# File core.core.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Version: 0.4.0
|
# Version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -16,7 +16,7 @@ from core import utils
|
|||||||
from core.Client import Client
|
from core.Client import Client
|
||||||
from core.tcp_server import TCPServer
|
from core.tcp_server import TCPServer
|
||||||
from core.udp_server import UDPServer
|
from core.udp_server import UDPServer
|
||||||
from modules import PluginsLoader
|
from modules import PluginsLoader, LuaPluginsLoader
|
||||||
from modules.WebAPISystem import app as webapp
|
from modules.WebAPISystem import app as webapp
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class Core:
|
|||||||
self.lock_upload = False
|
self.lock_upload = False
|
||||||
|
|
||||||
self.client_major_version = "2.0"
|
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)
|
ev.register_event("get_player", self.get_client)
|
||||||
|
|
||||||
@ -217,11 +217,14 @@ class Core:
|
|||||||
lambda x: f"Players list: {self.get_clients_list(True)}"
|
lambda x: f"Players list: {self.get_clients_list(True)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log.debug("Initializing PluginsLoader...")
|
pl_dir = "plugins"
|
||||||
if not os.path.exists("plugins"):
|
self.log.debug("Initializing PluginsLoaders...")
|
||||||
os.mkdir("plugins")
|
if not os.path.exists(pl_dir):
|
||||||
pl = PluginsLoader("plugins")
|
os.mkdir(pl_dir)
|
||||||
|
pl = PluginsLoader(pl_dir)
|
||||||
await pl.load()
|
await pl.load()
|
||||||
|
lpl = LuaPluginsLoader(pl_dir)
|
||||||
|
await lpl.load()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# WebApi Start
|
# WebApi Start
|
||||||
@ -263,6 +266,7 @@ class Core:
|
|||||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||||
|
|
||||||
await ev.call_async_event("_plugins_start")
|
await ev.call_async_event("_plugins_start")
|
||||||
|
await ev.call_async_event("_lua_plugins_start")
|
||||||
|
|
||||||
self.run = True
|
self.run = True
|
||||||
self.log.info(i18n.start)
|
self.log.info(i18n.start)
|
||||||
@ -287,6 +291,7 @@ class Core:
|
|||||||
ev.call_event("onServerStopped")
|
ev.call_event("onServerStopped")
|
||||||
await ev.call_async_event("onServerStopped")
|
await ev.call_async_event("onServerStopped")
|
||||||
await ev.call_async_event("_plugins_unload")
|
await ev.call_async_event("_plugins_unload")
|
||||||
|
await ev.call_async_event("_lua_plugins_unload")
|
||||||
self.run = False
|
self.run = False
|
||||||
self.log.info(i18n.stop)
|
self.log.info(i18n.stop)
|
||||||
if config.WebAPI["enabled"]:
|
if config.WebAPI["enabled"]:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.core.pyi
|
# File core.core.pyi
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Version 0.4.0
|
# Version 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.tcp_server.py
|
# File core.tcp_server.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.tcp_server.pyi
|
# File core.tcp_server.pyi
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.udp_server.py
|
# File core.udp_server.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -9,6 +9,7 @@ import asyncio
|
|||||||
from core import utils
|
from core import utils
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyProtectedMember
|
||||||
class UDPServer(asyncio.DatagramTransport):
|
class UDPServer(asyncio.DatagramTransport):
|
||||||
transport = None
|
transport = None
|
||||||
|
|
||||||
@ -20,11 +21,8 @@ class UDPServer(asyncio.DatagramTransport):
|
|||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.run = False
|
self.run = False
|
||||||
# self.transport = transport
|
|
||||||
|
|
||||||
def connection_made(self, transport):
|
def connection_made(self, transport): ...
|
||||||
self.log.debug("set connection transport")
|
|
||||||
# self.transport = self.transport()
|
|
||||||
|
|
||||||
async def handle_datagram(self, data, addr):
|
async def handle_datagram(self, data, addr):
|
||||||
try:
|
try:
|
||||||
@ -33,16 +31,15 @@ class UDPServer(asyncio.DatagramTransport):
|
|||||||
|
|
||||||
client = self.Core.get_client(cid=cid)
|
client = self.Core.get_client(cid=cid)
|
||||||
if client:
|
if client:
|
||||||
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:
|
match code:
|
||||||
case "p":
|
case "p": # Ping packet
|
||||||
self.log.debug(f"[{cid}] Send ping")
|
ev.call_event("onSentPing")
|
||||||
# TODO: Call event onSentPing
|
self.transport.sendto(b"p", addr)
|
||||||
self.transport.sendto(b"p", addr) # Send ping
|
case "Z": # Position packet
|
||||||
case "Z":
|
if client._udp_sock != (self.transport, addr):
|
||||||
# TODO: Call event onChangePosition
|
client._udp_sock = (self.transport, addr)
|
||||||
|
self.log.debug(f"Set UDP Sock for CID: {cid}")
|
||||||
|
ev.call_event("onChangePosition")
|
||||||
if client:
|
if client:
|
||||||
await client._send(data[2:], to_all=True, to_self=False, to_udp=True)
|
await client._send(data[2:], to_all=True, to_self=False, to_udp=True)
|
||||||
case _:
|
case _:
|
||||||
@ -86,7 +83,6 @@ class UDPServer(asyncio.DatagramTransport):
|
|||||||
self.run = True
|
self.run = True
|
||||||
while not self.transport.is_closing():
|
while not self.transport.is_closing():
|
||||||
await asyncio.sleep(0.2)
|
await asyncio.sleep(0.2)
|
||||||
self.log.info("UDP сервер сдох 1")
|
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
self.log.error("Cannot bind port or other error")
|
self.log.error("Cannot bind port or other error")
|
||||||
self.log.exception(e)
|
self.log.exception(e)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Developed by KuiToi Dev
|
# Developed by KuiToi Dev
|
||||||
# File core.udp_server.py
|
# File core.udp_server.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import asyncio
|
import asyncio
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# File core.utils.py
|
# File core.utils.py
|
||||||
# Written by: SantaSpeen
|
# Written by: SantaSpeen
|
||||||
# Version 1.1
|
# Version 1.1
|
||||||
# Core version: 0.4.0
|
# Core version: 0.4.1
|
||||||
# Licence: FPA
|
# Licence: FPA
|
||||||
# (c) kuitoi.su 2023
|
# (c) kuitoi.su 2023
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -28,7 +28,11 @@ class EventsSystem:
|
|||||||
"onPlayerJoin": [],
|
"onPlayerJoin": [],
|
||||||
"onChatReceive": [],
|
"onChatReceive": [],
|
||||||
"onCarSpawn": [],
|
"onCarSpawn": [],
|
||||||
|
"onCarDelete": [],
|
||||||
"onCarEdited": [],
|
"onCarEdited": [],
|
||||||
|
"onCarReset": [],
|
||||||
|
"onSentPing": [], # Only sync
|
||||||
|
"onChangePosition": [], # Only sync
|
||||||
"onServerStopped": [],
|
"onServerStopped": [],
|
||||||
}
|
}
|
||||||
self.__async_events = {
|
self.__async_events = {
|
||||||
@ -36,7 +40,9 @@ class EventsSystem:
|
|||||||
"onPlayerJoin": [],
|
"onPlayerJoin": [],
|
||||||
"onChatReceive": [],
|
"onChatReceive": [],
|
||||||
"onCarSpawn": [],
|
"onCarSpawn": [],
|
||||||
|
"onCarDelete": [],
|
||||||
"onCarEdited": [],
|
"onCarEdited": [],
|
||||||
|
"onCarReset": [],
|
||||||
"onServerStopped": []
|
"onServerStopped": []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +88,8 @@ class EventsSystem:
|
|||||||
return funcs_data
|
return funcs_data
|
||||||
|
|
||||||
def call_event(self, event_name, *args, **kwargs):
|
def call_event(self, event_name, *args, **kwargs):
|
||||||
self.log.debug(f"Calling sync event: '{event_name}'")
|
if event_name not in ["onChangePosition", "onSentPing"]: # UDP events
|
||||||
|
self.log.debug(f"Calling sync event: '{event_name}'")
|
||||||
funcs_data = []
|
funcs_data = []
|
||||||
|
|
||||||
if event_name in self.__events.keys():
|
if event_name in self.__events.keys():
|
||||||
|
@ -1 +1,2 @@
|
|||||||
from .plugins_loader import PluginsLoader
|
from .plugins_loader import PluginsLoader
|
||||||
|
from .lua_plugins_loader import LuaPluginsLoader
|
||||||
|
67
src/modules/PluginsLoader/lua_plugins_loader.py
Normal file
67
src/modules/PluginsLoader/lua_plugins_loader.py
Normal 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, _):
|
||||||
|
...
|
@ -33,20 +33,10 @@ class KuiToi:
|
|||||||
def name(self):
|
def name(self):
|
||||||
return self.__name
|
return self.__name
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, value):
|
|
||||||
# You chell not pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dir(self):
|
def dir(self):
|
||||||
return self.__dir
|
return self.__dir
|
||||||
|
|
||||||
@dir.setter
|
|
||||||
def dir(self, value):
|
|
||||||
# You chell not pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
@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 = os.path.join(self.__dir, file)
|
||||||
@ -83,6 +73,7 @@ class PluginsLoader:
|
|||||||
self.loaded_str = "Plugins: "
|
self.loaded_str = "Plugins: "
|
||||||
ev.register_event("_plugins_start", self.start)
|
ev.register_event("_plugins_start", self.start)
|
||||||
ev.register_event("_plugins_unload", self.unload)
|
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("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])
|
||||||
|
|
||||||
@ -90,14 +81,14 @@ class PluginsLoader:
|
|||||||
self.log.debug("Loading plugins...")
|
self.log.debug("Loading plugins...")
|
||||||
files = os.listdir(self.plugins_dir)
|
files = os.listdir(self.plugins_dir)
|
||||||
for file in files:
|
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:
|
try:
|
||||||
self.log.debug(f"Loading plugin: {file[:-3]}")
|
self.log.debug(f"Loading plugin: {file[:-3]}")
|
||||||
plugin = types.ModuleType(file[:-3])
|
plugin = types.ModuleType(file[:-3])
|
||||||
plugin.KuiToi = KuiToi
|
plugin.KuiToi = KuiToi
|
||||||
plugin.KuiToi._plugins_dir = self.plugins_dir
|
plugin.KuiToi._plugins_dir = self.plugins_dir
|
||||||
plugin.print = print
|
plugin.print = print
|
||||||
file_path = os.path.join(self.plugins_dir, file)
|
|
||||||
plugin.__file__ = file_path
|
plugin.__file__ = file_path
|
||||||
with open(f'{file_path}', 'r', encoding=config.enc) as f:
|
with open(f'{file_path}', 'r', encoding=config.enc) as f:
|
||||||
code = f.read()
|
code = f.read()
|
||||||
|
@ -11,5 +11,6 @@ from .ConfigProvider import ConfigProvider, Config
|
|||||||
from .i18n import MultiLanguage
|
from .i18n import MultiLanguage
|
||||||
from .EventsSystem import EventsSystem
|
from .EventsSystem import EventsSystem
|
||||||
from .PluginsLoader import PluginsLoader
|
from .PluginsLoader import PluginsLoader
|
||||||
|
from .PluginsLoader import LuaPluginsLoader
|
||||||
from .WebAPISystem import web_app
|
from .WebAPISystem import web_app
|
||||||
from .WebAPISystem import _stop as stop_web
|
from .WebAPISystem import _stop as stop_web
|
||||||
|
Loading…
x
Reference in New Issue
Block a user