Move code from case to func;

Add events for cars: onCarDelete, onCarReset;
Minor updates;
This commit is contained in:
Maxim Khomutov 2023-07-20 01:54:52 +03:00
parent 147e76e089
commit b6038ee6d0
3 changed files with 202 additions and 158 deletions

View File

@ -33,7 +33,8 @@ 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, None, None, None, None, None, None, None, None, None] # Max 10 cars
self._unicycle = None
self._connect_time: float = 0 self._connect_time: float = 0
@property @property
@ -173,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
@ -182,8 +183,8 @@ 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)
@ -307,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
@ -335,40 +334,32 @@ 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:]
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 car_id = 0
for c in self.cars: for car in self.cars:
if c is None: if not car:
break break
car_id += 1 car_id += 1
self.log.debug(f"Created a car: car_id={car_id}") self.log.debug(f"Created a car: car_id={car_id}")
car_data = data[2:]
car_json = {} car_json = {}
try: try:
car_json = json.loads(data[data.find("{"):]) car_json = json.loads(car_data[car_data.find("{"):])
except Exception as e: except Exception as e:
self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}") self.log.debug(f"Invalid car_json: Error: {e}; Data: {car_data}")
# allow = True allow = True
over_spawn = False 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) d2 = await ev.call_async_event("onCarSpawn", car=car_json, car_id=car_id, player=self)
ev_data.extend(d2) ev_data_list.extend(d2)
for d in ev_data: for ev_data in ev_data_list:
# TODO: handle event onCarSpawn # TODO: handle event onCarSpawn
pass pass
pkt = f"Os:{self.roles}:{self.nick}:{self.cid}-{car_id}:{car_data}" pkt = f"Os:{self.roles}:{self.nick}:{self.cid}-{car_id}:{car_data}"
unicycle = car_json.get("jbm") == "unicycle" unicycle = car_json.get("jbm") == "unicycle"
# FIXME: unicycle # FIXME: unicycle
# if (allow and (config.Game['max_cars'] > car_id or unicycle)) or over_spawn: # if (allow and (config.Game['max_cars'] > car_id or unicycle)) or over_spawn:
if config.Game['max_cars'] > car_id and not unicycle: if (allow and config.Game['max_cars'] > car_id and not unicycle) or over_spawn:
self.log.debug(f"Car spawn accepted.") self.log.debug(f"Car spawn accepted.")
self._cars[car_id] = { self._cars[car_id] = {
"packet": pkt, "packet": pkt,
@ -382,13 +373,22 @@ class Client:
await self._send(pkt) await self._send(pkt)
des = f"Od:{self.cid}-{car_id}" des = f"Od:{self.cid}-{car_id}"
await self._send(des) await self._send(des)
case "d": # Delete car
self.log.debug("Trying to delete car") async def _delete_car(self, raw_data):
cid, car_id = self._get_cid_vid(dta) cid, car_id = self._get_cid_vid(raw_data)
if car_id != -1 and cid == self.cid:
# TODO: Call event onCarDelete if car_id != -1 and self.cars[car_id]:
await self._send(dta, to_all=True, to_self=True)
try: 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] car = self.cars[car_id]
if car['unicycle']: if car['unicycle']:
self._cars.pop(car_id) self._cars.pop(car_id)
@ -396,12 +396,13 @@ class Client:
self._cars[car_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}")
self.log.debug(f"Deleted car: car_id={car_id}") self.log.debug(f"Deleted car: car_id={car_id}")
except IndexError:
self.log.debug(f"Unknown car: car_id={car_id}") else:
case "c": # Edit car self.log.debug(f"Invalid car: car_id={car_id}")
self.log.debug("Trying to edit car")
cid, car_id = self._get_cid_vid(dta) async def _edit_car(self, raw_data, data):
try: 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) client = self.__Core.get_client(cid=cid)
if client: if client:
car = client.cars[car_id] car = client.cars[car_id]
@ -412,60 +413,69 @@ class Client:
self.log.debug(f"Invalid new_car_json: Error: {e}; Data: {data}") self.log.debug(f"Invalid new_car_json: Error: {e}; Data: {data}")
allow = False 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) d2 = await ev.call_async_event("onCarEdited", car=new_car_json, car_id=car_id, player=self)
ev_data.extend(d2) ev_data_list.extend(d2)
for d in ev_data: for ev_data in ev_data_list:
# TODO: handle event onCarEdited # TODO: handle event onCarEdited
pass pass
if car_id != -1 and cid == self.cid or allow: if cid == self.cid or allow or admin_allow:
if car['unicycle']: if car['unicycle']:
self._cars.pop(car_id) self._cars.pop(car_id)
await self._send(f"Od:{cid}-{car_id}", to_all=True, to_self=True) await self._send(f"Od:{cid}-{car_id}", to_all=True, to_self=True)
else: 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']: if car['json_ok']:
old_car_json = car['json'] old_car_json = car['json']
old_car_json.update(new_car_json) old_car_json.update(new_car_json)
car['json'] = old_car_json car['json'] = old_car_json
self.log.debug(f"Updated car: car_id={car_id}") self.log.debug(f"Updated car: car_id={car_id}")
except IndexError: else:
self.log.debug(f"Unknown car: car_id={car_id}") 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(data)
case "c": # Edit car
self.log.debug("Trying to edit car")
await self._edit_car(raw_data, data)
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
await self._send(dta, to_all=True, to_self=False)
self.log.debug(f"Car reset: car_id={car_id}")
case "t": case "t":
self.log.debug(f"Received 'Ot' packet: {dta}") self.log.debug(f"Received 'Ot' packet: {raw_data}")
await self._send(dta, to_all=True, to_self=False) await self._send(raw_data, to_all=True, to_self=False)
case "m": case "m":
await self._send(dta, to_all=True, to_self=True) self.log.debug(f"Received 'Om' packet: {raw_data}")
await self._send(raw_data, to_all=True, to_self=True)
async def _handle_codes(self, data): async def _connected_handler(self):
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
code = data[0]
# Codes: p, Z in udp_server.py
match code:
case "H":
self.log.info(f"Syncing time: {round(time.monotonic() - self._connect_time, 2)}s") self.log.info(f"Syncing time: {round(time.monotonic() - self._connect_time, 2)}s")
# Client connected # Client connected
ev.call_event("onPlayerJoin", player=self) ev.call_event("onPlayerJoin", player=self)
@ -483,7 +493,7 @@ class Client:
continue continue
await self._send(car['packet']) await self._send(car['packet'])
case "C": # Chat handler async def _chat_handler(self, data):
sup = data.find(":", 2) sup = data.find(":", 2)
if sup == -1: if sup == -1:
await self._send("C:Server: Invalid message.") await self._send("C:Server: Invalid message.")
@ -491,7 +501,6 @@ class Client:
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")
return return
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)
@ -511,13 +520,39 @@ class Client:
if to_client: if to_client:
# noinspection PyProtectedMember # noinspection PyProtectedMember
writer = to_client._writer 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) await self._send(f"C:{message}", to_all=to_all, to_self=to_self, writer=writer)
need_send = False need_send = False
except KeyError | AttributeError: except KeyError | AttributeError:
self.log.error(f"Returns invalid data: {ev_data}") self.log.error(f"Returns invalid data: {ev_data}")
if need_send: if need_send:
self.log.info(f"{self.nick}: {msg}")
await self._send(data, to_all=True) 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 case "O": # Cars handler
await self._handle_car_codes(data) await self._handle_car_codes(data)

View File

@ -7,7 +7,7 @@
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
from core import Core, utils from core import Core, utils
@ -33,7 +33,7 @@ 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]]] = []
@property @property
def _writer(self) -> StreamWriter: ... def _writer(self) -> StreamWriter: ...
@property @property
@ -56,12 +56,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: ...

View File

@ -28,7 +28,9 @@ class EventsSystem:
"onPlayerJoin": [], "onPlayerJoin": [],
"onChatReceive": [], "onChatReceive": [],
"onCarSpawn": [], "onCarSpawn": [],
"onCarDelete": [],
"onCarEdited": [], "onCarEdited": [],
"onCarReset": [],
"onServerStopped": [], "onServerStopped": [],
} }
self.__async_events = { self.__async_events = {
@ -36,7 +38,9 @@ class EventsSystem:
"onPlayerJoin": [], "onPlayerJoin": [],
"onChatReceive": [], "onChatReceive": [],
"onCarSpawn": [], "onCarSpawn": [],
"onCarDelete": [],
"onCarEdited": [], "onCarEdited": [],
"onCarReset": [],
"onServerStopped": [] "onServerStopped": []
} }