Recreate cars system;

Move code "O" to client._handle_vehicle_codes();
This commit is contained in:
Maxim Khomutov 2023-07-17 14:18:52 +03:00
parent c9e6a0a9cd
commit 50b1e7b176
2 changed files with 178 additions and 120 deletions

View File

@ -110,10 +110,13 @@ class Client:
continue continue
if not to_udp or code in ['V', 'W', 'Y', 'E']: if not to_udp or code in ['V', 'W', 'Y', 'E']:
if code in ['O', 'T'] or len(data) > 1000: if code in ['O', 'T'] or len(data) > 1000:
if len(data) > 400:
# TODO: Compress data # TODO: Compress data
await client._send(data) await client._send(data)
else: else:
await client._send(data) await client._send(data)
else:
await client._send(data)
else: else:
# TODO: UDP send # TODO: UDP send
self.log.debug(f"UDP Part not ready: {code}") self.log.debug(f"UDP Part not ready: {code}")
@ -159,7 +162,7 @@ class Client:
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: {data}") self.log.debug(f"ABG Packet: {len(data)}")
return data return data
return data return data
except ConnectionError: except ConnectionError:
@ -247,30 +250,115 @@ class Client:
await self._send(bytes(mod_list, "utf-8")) await self._send(bytes(mod_list, "utf-8"))
elif data == b"Done": elif data == b"Done":
await self._send(b"M/levels/" + bytes(config.Game['map'], 'utf-8') + b"/info.json") await self._send(b"M/levels/" + bytes(config.Game['map'], 'utf-8') + b"/info.json")
for c in range(config.Game['max_cars']):
self._cars.append(None)
break break
return return
async def _looper(self): def _get_cid_vid(self, data: str):
await self._send(b"P" + bytes(f"{self.cid}", "utf-8")) # Send clientID s = data[data.find(":", 1)+1:]
await self._sync_resources() id_sep = s.find('-')
while self.__alive: if id_sep == -1:
data = await self._recv() self.log.debug(f"Invalid packet: Could not parse pid/vid from packet, as there is no '-' separator: '{s}'")
return -1, -1
cid = s[:id_sep]
vid = s[id_sep + 1:]
if cid.isdigit() and vid.isdigit():
try:
cid = int(cid)
vid = int(vid)
return cid, vid
except ValueError:
self.log.debug(f"Invalid packet: Could not parse cid/vid from packet, as one or both are not valid "
f"numbers: '{s}'")
return -1, -1
self.log.debug(f"Invalid packet: Could not parse pid/vid from packet: '{s}'")
return -1, -1
async def _handle_vehicle_codes(self, data):
if len(data) < 6:
return
sub_code = data[1]
data = data[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 with ID {car_id}")
car_data = data[2:]
car_json = {}
try:
car_json = json.loads(data[5:])
except Exception as e:
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
# TODO: Call event onCarSpawn
allow_spawn = True
over_spawn = False
pkt = f"Os:{self.roles}:{self.nick}:{self.cid}-{car_id}:{car_data}"
unicycle = car_json.get("jbm") == "unicycle"
if (allow_spawn and (config.Game['max_cars'] > car_id or unicycle)) or over_spawn:
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)
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(data)
if car_id != -1 and cid == self.cid:
# TODO: Call event onCarDelete
await self._send(data, to_all=True, to_self=True)
try:
self._cars[car_id] = None
await self._send(f"Od:{self.cid}-{car_id}")
self.log.debug(f"Deleted car with 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")
# TODO: edit car
cid, car_id = self._get_cid_vid(data)
if car_id != -1 and cid == self.cid:
car = self.cars[car_id]
if car['unicycle']:
pass
case "r": # Reset car
self.log.debug("Trying to reset car")
# TODO: reset car
cid, car_id = self._get_cid_vid(data)
if car_id != -1 and cid == self.cid:
pass
case "t" | "m":
pass
async def _handle_codes(self, data):
if not data: if not data:
self.__alive = False self.__alive = False
break return
# 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)
data = data.decode('utf-8') data = data.decode()
code = data[0] code = data[0]
self.log.debug(f"Received code: {code}, data: {data}")
match code: match code:
case "H": case "H":
# Client connected # Client connected
ev.call_event("onPlayerJoin", player=self) ev.call_event("onPlayerJoin", player=self)
await ev.call_async_event("onPlayerJoin", player=self) await ev.call_async_event("onPlayerJoin", player=self)
@ -282,14 +370,14 @@ class Client:
if not client: if not client:
continue continue
for car in client.cars: for car in client.cars:
await self._send(car) await self._send(car['packet'])
case "C": # Chat handler case "C": # Chat handler
msg = data[4 + len(self.nick):] msg = data[4 + len(self.nick):]
if not msg: if not msg:
self.log.debug("Tried to send an empty event, ignoring") self.log.debug("Tried to send an empty event, ignoring")
continue return
self.log.info(f"Received message: {msg}") self.log.info(f"{self.nick}: {msg}")
to_ev = {"message": msg, "player": self} to_ev = {"message": msg, "player": self}
ev_data_list = ev.call_event("onChatReceive", **to_ev) ev_data_list = ev.call_event("onChatReceive", **to_ev)
d2 = await ev.call_async_event("onChatReceive", **to_ev) d2 = await ev.call_async_event("onChatReceive", **to_ev)
@ -319,55 +407,22 @@ class Client:
if need_send: if need_send:
await self._send(data, to_all=True) await self._send(data, to_all=True)
case "O": # Vehicle info handler case "O": # Cars handler
if len(data) < 6: await self._handle_vehicle_codes(data)
continue
sub_code = data[1]
data = data[3:]
vid = -1
pid = -1
match sub_code:
case "s": # Spawn car
if data[0] == "0":
car_id = len(self._cars)
self.log.debug(f"Created a car with ID {car_id}")
car_data = data[2:]
car_json = {}
try:
car_json = json.loads(data[5:])
except Exception as e:
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
# TODO: Call event onVehicleSpawn
spawn = True
pkt = f"Os:{self.roles}:{self.nick}:{self.cid}-{car_id}:{car_data}"
if spawn and (config.Game['max_cars'] > car_id or car_json.get("jbm") == "unicycle"):
self.log.debug(f"Car spawn accepted.")
self._cars.append(car_data)
await self._send(pkt, to_all=True)
else:
await self._send(pkt)
des = f"Od:{self.cid}-{car_id}"
await self._send(des)
case "c": # Edit car
# TODO: edit car
pass
case "d": # Delete car
# TODO: delete car
pass
case "r": # Reset car
# TODO: reset car
pass
case "t" | "m":
pass
case "E": # Client events handler case "E": # Client events handler
# TODO: HandleEvent # TODO: HandleEvent
pass pass
case "N": case "N":
# TODO: N await self._send(data, to_all=True, to_self=False)
pass
async def _looper(self):
await self._send(f"P{self.cid}") # Send clientID
await self._sync_resources()
while self.__alive:
data = await self._recv()
self._loop.create_task(self._handle_codes(data))
async def _remove_me(self): async def _remove_me(self):
await asyncio.sleep(0.3) await asyncio.sleep(0.3)

View File

@ -7,7 +7,7 @@
import asyncio import asyncio
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from logging import Logger from logging import Logger
from typing import Tuple from typing import Tuple, List, Dict
from core import Core, utils from core import Core, utils
@ -29,7 +29,7 @@ class Client:
self._guest = True self._guest = True
self.__alive = True self.__alive = True
self._ready = False self._ready = False
self._cars = [] self._cars: List[dict | None] = []
@property @property
def _writer(self) -> StreamWriter: ... def _writer(self) -> StreamWriter: ...
@property @property
@ -45,13 +45,16 @@ class Client:
@property @property
def ready(self) -> bool: ... def ready(self) -> bool: ...
@property @property
def cars(self) -> list: ... def cars(self) -> List[dict | None]: ...
def is_disconnected(self) -> bool: ... def is_disconnected(self) -> bool: ...
async def kick(self, reason: str) -> None: ... async def kick(self, reason: 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 _recv(self) -> bytes: ... async def _recv(self) -> bytes: ...
async def _split_load(self, start: int, end: int, d_sock: bool, filename: str) -> None: ... async def _split_load(self, start: int, end: int, d_sock: bool, filename: str) -> None: ...
async def _get_cid_vid(self, s: str) -> Tuple[int, int]: ...
async def _handle_vehicle_codes(self, data) -> None: ...
async def _handle_codes(self, data) -> 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: ...