mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2026-02-16 02:20:52 +00:00
[+] PPS
[+] Clients ticks [>] UDP handler to Client class [~] Minor
This commit is contained in:
@@ -9,7 +9,7 @@ import json
|
||||
import math
|
||||
import time
|
||||
import zlib
|
||||
from asyncio import Lock
|
||||
from asyncio import Queue
|
||||
|
||||
from core import utils
|
||||
|
||||
@@ -21,7 +21,19 @@ class Client:
|
||||
self.__writer = writer
|
||||
self._core = core
|
||||
self.__alive = True
|
||||
self.__packets_queue = []
|
||||
|
||||
self.__queue_tpc = Queue()
|
||||
self.__queue_udp = Queue()
|
||||
|
||||
self._tpc_count = 0
|
||||
self._udp_count = 0
|
||||
self._tpc_count_total = 0
|
||||
self._udp_count_total = 0
|
||||
self._udp_size_total = 0
|
||||
self._tpc_size_total = 0
|
||||
self.tcp_pps = 0
|
||||
self.udp_pps = 0
|
||||
|
||||
self.__tasks = []
|
||||
self._down_sock = (None, None)
|
||||
self._udp_sock = (None, None)
|
||||
@@ -41,7 +53,7 @@ class Client:
|
||||
self._unicycle = {"id": -1, "packet": ""}
|
||||
self._connect_time = 0
|
||||
self._last_position = {}
|
||||
self._lock = Lock()
|
||||
self._last_recv = time.monotonic()
|
||||
|
||||
@property
|
||||
def _writer(self):
|
||||
@@ -132,7 +144,7 @@ class Client:
|
||||
to_all = False
|
||||
await self._send(f"C:{message}", to_all=to_all)
|
||||
|
||||
async def send_event(self, event_name, event_data, to_all=True):
|
||||
async def send_event(self, event_name, event_data, to_all=False):
|
||||
self.log.debug(f"send_event: {event_name}:{event_data}; {to_all=}")
|
||||
if not self.ready:
|
||||
self.log.debug(f"Client not ready.")
|
||||
@@ -142,7 +154,7 @@ class Client:
|
||||
if len(event_data) > 104857599:
|
||||
self.log.error("Client data too big! >=104857599")
|
||||
return
|
||||
await self._send(f"E:{event_name}:{event_data}", to_all=to_all)
|
||||
await self._send(f"E:{event_name}:{event_data}", to_all, True)
|
||||
|
||||
async def _send(self, data, to_all=False, to_self=True, to_udp=False, writer=None):
|
||||
|
||||
@@ -181,7 +193,7 @@ class Client:
|
||||
if udp_sock and udp_addr:
|
||||
try:
|
||||
if not udp_sock.is_closing():
|
||||
# self.log.debug(f'[UDP] {data!r}')
|
||||
# self.log.debug(f'[UDP] {data!r}; {udp_addr}')
|
||||
udp_sock.sendto(data, udp_addr)
|
||||
except OSError:
|
||||
self.log.debug("[UDP] Error sending")
|
||||
@@ -220,12 +232,11 @@ class Client:
|
||||
self.is_disconnected()
|
||||
if self.__alive:
|
||||
if header == b"":
|
||||
self.__packets_queue.append(None)
|
||||
await self._tpc_put(None)
|
||||
self.__alive = False
|
||||
continue
|
||||
self.log.error(f"Header: {header}")
|
||||
await self.kick("Invalid packet - header negative")
|
||||
self.__packets_queue.append(None)
|
||||
continue
|
||||
|
||||
if int_header > 100 * MB:
|
||||
@@ -233,7 +244,6 @@ class Client:
|
||||
self.log.warning("Client sent header of >100MB - "
|
||||
"assuming malicious intent and disconnecting the client.")
|
||||
self.log.error(f"Last recv: {await self.__reader.read(100 * MB)}")
|
||||
self.__packets_queue.append(None)
|
||||
continue
|
||||
|
||||
data = b""
|
||||
@@ -251,11 +261,11 @@ class Client:
|
||||
|
||||
if one:
|
||||
return data
|
||||
self.__packets_queue.append(data)
|
||||
await self._tpc_put(data)
|
||||
|
||||
except ConnectionError:
|
||||
self.__alive = False
|
||||
self.__packets_queue.append(None)
|
||||
await self._tpc_put(None)
|
||||
|
||||
async def _split_load(self, start, end, d_sock, filename, speed_limit=None):
|
||||
real_size = end - start
|
||||
@@ -656,8 +666,8 @@ class Client:
|
||||
self.log.info(f"{self.nick}: {msg}")
|
||||
await self._send(data, to_all=True)
|
||||
|
||||
async def _handle_codes(self, data):
|
||||
if not data:
|
||||
async def _handle_codes_tcp(self, data):
|
||||
if data is None:
|
||||
self.__alive = False
|
||||
return
|
||||
|
||||
@@ -676,17 +686,14 @@ class Client:
|
||||
match data[0]: # At data[0] code
|
||||
case "H": # Map load, client ready
|
||||
await self._connected_handler()
|
||||
|
||||
case "C": # Chat handler
|
||||
if _bytes:
|
||||
return
|
||||
await self._chat_handler(data)
|
||||
|
||||
case "O": # Cars handler
|
||||
if _bytes:
|
||||
return
|
||||
await self._handle_car_codes(data)
|
||||
|
||||
case "E": # Client events handler
|
||||
if len(data) < 2:
|
||||
self.log.debug("Tried to send an empty event, ignoring.")
|
||||
@@ -704,7 +711,75 @@ class Client:
|
||||
ev.call_event(event_name, data=even_data, player=self)
|
||||
await ev.call_async_event(event_name, data=even_data, player=self)
|
||||
case _:
|
||||
self.log.warning(f"TCP [{self.cid}] Unknown code: {data[0]}; {data}")
|
||||
self.log.warning(f"TCP Unknown code: {data[0]}; {data}")
|
||||
|
||||
async def _handle_codes_udp(self, data):
|
||||
code = data[2:3].decode()
|
||||
data = data[2:].decode()
|
||||
match code:
|
||||
case "p": # Ping packet
|
||||
ev.call_event("onSentPing", player=self)
|
||||
await self._send(b"p", to_udp=True)
|
||||
case "Z": # Position packet
|
||||
sub = data.find("{", 1)
|
||||
last_pos = data[sub:]
|
||||
try:
|
||||
_, car_id = self._get_cid_vid(data)
|
||||
if self._cars[car_id]:
|
||||
last_pos = json.loads(last_pos)
|
||||
self._last_position = last_pos
|
||||
self._cars[car_id]['pos'] = last_pos
|
||||
ev.call_event("onChangePosition", data, player=self, pos=last_pos)
|
||||
except Exception as e:
|
||||
self.log.warning(f"Cannot parse position packet: {e}")
|
||||
self.log.debug(f"data: '{data}', sub: {sub}")
|
||||
self.log.debug(f"last_pos ({type(last_pos)}): {last_pos}")
|
||||
await self._send(data, True, False, True)
|
||||
case "X":
|
||||
await self._send(data, True, False, True)
|
||||
case _:
|
||||
self.log.warning(f"UDP Unknown code: {code}; {data}")
|
||||
|
||||
def _tick_pps(self, _):
|
||||
self.tcp_pps = self._tpc_count
|
||||
self.udp_pps = self._udp_count
|
||||
self._tpc_count = 0
|
||||
self._udp_count = 0
|
||||
if self.tcp_pps > self._core.target_tps or self.udp_pps > self._core.target_tps:
|
||||
self.log.warning(f"PPS > TPS; PPS: TPC: {self.tcp_pps}, UDP: {self.udp_pps}")
|
||||
|
||||
async def __tick_player_tpc(self, _):
|
||||
try:
|
||||
if self.__queue_tpc.qsize() > 0:
|
||||
packet = await self.__queue_tpc.get()
|
||||
if packet is None:
|
||||
return await self._remove_me()
|
||||
await self._handle_codes_tcp(packet)
|
||||
except Exception as e:
|
||||
self.log.error(f'[TPC] Error while ticking player:')
|
||||
self.log.exception(e)
|
||||
|
||||
async def __tick_player_udp(self, _):
|
||||
try:
|
||||
if self.__queue_udp.qsize() > 0:
|
||||
packet = await self.__queue_udp.get()
|
||||
await self._handle_codes_udp(packet)
|
||||
except Exception as e:
|
||||
self.log.error(f'[UDP] Error while ticking player:')
|
||||
self.log.exception(e)
|
||||
|
||||
async def _tpc_put(self, packet):
|
||||
if packet:
|
||||
self._tpc_count += 1
|
||||
self._tpc_count_total += 1
|
||||
self._tpc_size_total += len(packet)
|
||||
await self.__queue_tpc.put(packet)
|
||||
|
||||
async def _udp_put(self, packet):
|
||||
self._udp_count += 1
|
||||
self._udp_count_total += 1
|
||||
self._udp_size_total += len(packet)
|
||||
await self.__queue_udp.put(packet)
|
||||
|
||||
async def _looper(self):
|
||||
ev.call_lua_event("onPlayerConnecting", self.cid)
|
||||
@@ -712,58 +787,47 @@ class Client:
|
||||
await self._send(f"P{self.cid}") # Send clientID
|
||||
await self._sync_resources()
|
||||
ev.call_lua_event("onPlayerJoining", self.cid)
|
||||
tasks = self.__tasks
|
||||
recv = asyncio.create_task(self._recv())
|
||||
tasks.append(recv)
|
||||
self._synced = True
|
||||
while self.__alive:
|
||||
if len(self.__packets_queue) > 0:
|
||||
for index, packet in enumerate(self.__packets_queue):
|
||||
# self.log.debug(f"Packet: {packet}")
|
||||
del self.__packets_queue[index]
|
||||
task = self._loop.create_task(self._handle_codes(packet))
|
||||
tasks.append(task)
|
||||
else:
|
||||
await asyncio.sleep(0.1)
|
||||
await asyncio.gather(*tasks)
|
||||
ev.register("serverTick", self.__tick_player_tpc)
|
||||
ev.register("serverTick", self.__tick_player_udp)
|
||||
ev.register("serverTick_1s", self._tick_pps)
|
||||
await self._recv()
|
||||
|
||||
async def _remove_me(self):
|
||||
await asyncio.sleep(0.3)
|
||||
self.__alive = False
|
||||
if (self.cid > 0 or self.nick is not None) and \
|
||||
self._core.clients_by_nick.get(self.nick):
|
||||
if self._core.clients_by_nick.get(self.nick):
|
||||
for i, car in enumerate(self._cars):
|
||||
if not car:
|
||||
continue
|
||||
self.log.debug(f"Removing car: car_id={i}")
|
||||
await self._send(f"Od:{self.cid}-{i}", to_all=True, to_self=False)
|
||||
if self.ready:
|
||||
await self._send(f"J{self.nick} disconnected!", to_all=True, to_self=False) # I'm disconnected.
|
||||
await self._send(f"J{self.nick} disconnected!", to_all=True, to_self=False)
|
||||
self.log.debug(f"Removing client")
|
||||
ev.call_lua_event("onPlayerDisconnect", self.cid)
|
||||
ev.call_event("onPlayerDisconnect", player=self)
|
||||
await ev.call_async_event("onPlayerDisconnect", player=self)
|
||||
|
||||
self.log.info(
|
||||
i18n.client_player_disconnected.format(
|
||||
round((time.monotonic() - self._connect_time) / 60, 2)
|
||||
)
|
||||
)
|
||||
ev.unregister(self.__tick_player_tpc)
|
||||
ev.unregister(self.__tick_player_udp)
|
||||
ev.unregister(self._tick_pps)
|
||||
gt = round((time.monotonic() - self._connect_time) / 60, 2)
|
||||
self.log.info(i18n.client_player_disconnected.format(gt))
|
||||
self._core.clients[self.cid] = None
|
||||
del self._core.clients_by_id[self.cid]
|
||||
del self._core.clients_by_nick[self.nick]
|
||||
else:
|
||||
self.log.debug(f"Removing client; Closing connection...")
|
||||
self.log.debug(f"TPC: Packets: {self._tpc_count_total}; Size: {self._tpc_size_total}")
|
||||
self.log.debug(f"UDP: Packets: {self._udp_size_total}; Size: {self._udp_size_total}")
|
||||
await asyncio.sleep(0.001)
|
||||
try:
|
||||
if not self.__writer.is_closing():
|
||||
self.__writer.close()
|
||||
await self.__writer.wait_closed()
|
||||
self.__writer.close()
|
||||
await self.__writer.wait_closed()
|
||||
except Exception as e:
|
||||
self.log.debug(f"Error while closing writer: {e}")
|
||||
try:
|
||||
_, down_w = self._down_sock
|
||||
if down_w and not down_w.is_closing():
|
||||
down_w.close()
|
||||
await down_w.wait_closed()
|
||||
down_w.close()
|
||||
await down_w.wait_closed()
|
||||
except Exception as e:
|
||||
self.log.debug(f"Error while closing download writer: {e}")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Licence: FPA
|
||||
# (c) kuitoi.su 2023
|
||||
import asyncio
|
||||
from asyncio import StreamReader, StreamWriter, DatagramTransport, Lock
|
||||
from asyncio import StreamReader, StreamWriter, DatagramTransport, Lock, Queue
|
||||
from logging import Logger
|
||||
from typing import Tuple, List, Dict, Optional, Union, Any
|
||||
|
||||
@@ -19,7 +19,16 @@ class Client:
|
||||
self.__tasks = []
|
||||
self.__reader = reader
|
||||
self.__writer = writer
|
||||
self.__packets_queue = []
|
||||
self.__queue_tpc = Queue()
|
||||
self.__queue_udp = Queue()
|
||||
self._tpc_count = 0
|
||||
self._udp_count = 0
|
||||
self._tpc_count_total = 0
|
||||
self._udp_count_total = 0
|
||||
self._udp_size_total = 0
|
||||
self._tpc_size_total = 0
|
||||
self.tcp_pps = 0
|
||||
self.udp_pps = 0
|
||||
self._udp_sock: Tuple[DatagramTransport | None, Tuple[str, int] | None] = (None, None)
|
||||
self._down_sock: Tuple[StreamReader | None, StreamWriter | None] = (None, None)
|
||||
self._log = utils.get_logger("client(id: )")
|
||||
@@ -87,7 +96,13 @@ class Client:
|
||||
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 _handle_codes_tcp(self, data: bytes) -> None: ...
|
||||
async def _handle_codes_udp(self, data: bytes) -> None: ...
|
||||
def _tick_pps(self, _): ...
|
||||
async def __tick_player_tpc(self, _): ...
|
||||
async def __tick_player_udp(self, _): ...
|
||||
async def _tpc_put(self, data): ...
|
||||
async def _udp_put(self, data): ...
|
||||
async def _looper(self) -> None: ...
|
||||
def _update_logger(self) -> None: ...
|
||||
async def _remove_me(self) -> None: ...
|
||||
|
||||
@@ -10,7 +10,7 @@ __title__ = 'KuiToi-Server'
|
||||
__description__ = 'BeamingDrive Multiplayer server compatible with BeamMP clients.'
|
||||
__url__ = 'https://github.com/kuitoi/kuitoi-Server'
|
||||
__version__ = '0.4.8 (pre)'
|
||||
__build__ = 2542 # Я это считаю лог файлами
|
||||
__build__ = 2663 # Я это считаю лог файлами
|
||||
__author__ = 'SantaSpeen'
|
||||
__author_email__ = 'admin@kuitoi.su'
|
||||
__license__ = "FPA"
|
||||
@@ -18,6 +18,7 @@ __copyright__ = 'Copyright 2024 © SantaSpeen (Maxim Khomutov)'
|
||||
|
||||
import asyncio
|
||||
import builtins
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
import prompt_toolkit.shortcuts as shortcuts
|
||||
@@ -36,6 +37,8 @@ if args.version:
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
if sys.platform == 'win32':
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
log = get_logger("core.init")
|
||||
|
||||
# Config file init
|
||||
|
||||
@@ -26,6 +26,7 @@ def calc_ticks(ticks, duration):
|
||||
ticks.popleft()
|
||||
return len(ticks) / duration
|
||||
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
@@ -50,7 +51,11 @@ class Core:
|
||||
self.tcp = TCPServer
|
||||
self.udp = UDPServer
|
||||
|
||||
self.tcp_pps = 0
|
||||
self.udp_pps = 0
|
||||
|
||||
self.tps = 10
|
||||
self.target_tps = 60
|
||||
|
||||
self.lock_upload = False
|
||||
|
||||
@@ -142,6 +147,12 @@ class Core:
|
||||
continue
|
||||
await client.kick("Server shutdown!")
|
||||
|
||||
async def __gracefully_remove(self):
|
||||
for client in self.clients:
|
||||
if not client:
|
||||
continue
|
||||
await client._remove_me()
|
||||
|
||||
# noinspection SpellCheckingInspection,PyPep8Naming
|
||||
async def heartbeat(self, test=False):
|
||||
try:
|
||||
@@ -257,7 +268,6 @@ class Core:
|
||||
return "Client not found."
|
||||
|
||||
async def _useful_ticks(self, _):
|
||||
target_tps = 20
|
||||
tasks = []
|
||||
self.tick_counter += 1
|
||||
events = {
|
||||
@@ -272,32 +282,34 @@ class Core:
|
||||
60: "serverTick_60s"
|
||||
}
|
||||
for interval in sorted(events.keys(), reverse=True):
|
||||
if self.tick_counter % (interval * target_tps) == 0:
|
||||
if self.tick_counter % (interval * self.target_tps) == 0:
|
||||
ev.call_event(events[interval])
|
||||
tasks.append(ev.call_async_event(events[interval]))
|
||||
await asyncio.gather(*tasks)
|
||||
if self.tick_counter == (60 * target_tps):
|
||||
if self.tick_counter == (60 * self.target_tps):
|
||||
self.tick_counter = 0
|
||||
|
||||
async def _tick(self):
|
||||
try:
|
||||
ticks = 0
|
||||
target_tps = 20
|
||||
target_interval = 1 / target_tps
|
||||
target_tps = self.target_tps
|
||||
last_tick_time = time.monotonic()
|
||||
ev.register("serverTick", self._useful_ticks)
|
||||
ticks_2s = deque(maxlen=2 * int(target_tps) + 1)
|
||||
ticks_5s = deque(maxlen=5 * int(target_tps) + 1)
|
||||
ticks_30s = deque(maxlen=30 * int(target_tps) + 1)
|
||||
ticks_60s = deque(maxlen=60 * int(target_tps) + 1)
|
||||
console.add_command("tps", lambda
|
||||
_: f"{calc_ticks(ticks_2s, 2):.2f}TPS; last: {calc_ticks(ticks_5s, 5):.2f}TPS/5s; {calc_ticks(ticks_30s, 30):.2f}TPS/30s; {calc_ticks(ticks_60s, 60):.2f}TPS/60s;",
|
||||
console.add_command("tps", lambda _: f"{calc_ticks(ticks_2s, 2):.2f}TPS; For last 5s, 30s, 60s: "
|
||||
f"last: {calc_ticks(ticks_5s, 5):.2f},"
|
||||
f"{calc_ticks(ticks_30s, 30):.2f},"
|
||||
f"{calc_ticks(ticks_60s, 60):.2f}.",
|
||||
None, "Print TPS", {"tps": None})
|
||||
_add_to_sleep = deque(maxlen=13 * int(target_tps))
|
||||
_add_to_sleep.append(0.013)
|
||||
_add_to_sleep = deque([0.0, 0.0, 0.0,], maxlen=3 * int(target_tps))
|
||||
_t0 = []
|
||||
|
||||
self.log.debug("tick system started")
|
||||
while self.run:
|
||||
target_interval = 1 / self.target_tps
|
||||
start_time = time.monotonic()
|
||||
|
||||
ev.call_event("serverTick")
|
||||
@@ -306,6 +318,7 @@ class Core:
|
||||
# Calculate the time taken for this tick
|
||||
end_time = time.monotonic()
|
||||
tick_duration = end_time - start_time
|
||||
_t0.append(tick_duration)
|
||||
|
||||
# Calculate the time to sleep to maintain target TPS
|
||||
sleep_time = target_interval - tick_duration - statistics.fmean(_add_to_sleep)
|
||||
@@ -327,14 +340,14 @@ class Core:
|
||||
# if self.tps < 5:
|
||||
# self.log.warning(f"Low TPS: {self.tps:.2f}")
|
||||
# Reset for next calculation
|
||||
_t0s = max(_t0), min(_t0), statistics.fmean(_t0)
|
||||
_tw = max(_add_to_sleep), min(_add_to_sleep), statistics.fmean(_add_to_sleep)
|
||||
self.log.debug(f"[{'OK' if sleep_time > 0 else "CHECK"}] TPS: {self.tps:.2f}; Tt={_t0s}; Ts={sleep_time}; Tw={_tw}")
|
||||
_t0 = []
|
||||
last_tick_time = current_time
|
||||
ticks = 0
|
||||
|
||||
tw = time.monotonic() - start_time - sleep_time
|
||||
_add_to_sleep.append(tw)
|
||||
# if elapsed_time >= 1:
|
||||
# self.log.debug(
|
||||
# f"ts: {sleep_time}; tw: {tw}; tw-ts: {tw - sleep_time} ({statistics.fmean(_add_to_sleep)});")
|
||||
_add_to_sleep.append(time.monotonic() - start_time - sleep_time)
|
||||
self.log.debug("tick system stopped")
|
||||
except Exception as e:
|
||||
self.log.exception(e)
|
||||
@@ -377,7 +390,7 @@ class Core:
|
||||
self.log.info(i18n.init_ok)
|
||||
|
||||
await self.heartbeat(True)
|
||||
for i in range(int(config.Game["players"] * 2.3)): # * 2.3 For down sock and buffer.
|
||||
for i in range(int(config.Game["players"] * 4)): # * 4 For down sock and buffer.
|
||||
self.clients.append(None)
|
||||
tasks = []
|
||||
ev.register("serverTick_1s", self._check_alive)
|
||||
@@ -402,7 +415,7 @@ class Core:
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.log.error(f"Exception: {e}")
|
||||
self.log.error(f"Exception in main:")
|
||||
self.log.exception(e)
|
||||
finally:
|
||||
await self.stop()
|
||||
@@ -413,19 +426,24 @@ class Core:
|
||||
async def stop(self):
|
||||
self.run = False
|
||||
ev.call_lua_event("onShutdown")
|
||||
await self.__gracefully_kick()
|
||||
self.tcp.stop()
|
||||
self.udp._stop()
|
||||
ev.call_event("onServerStopped")
|
||||
await ev.call_async_event("onServerStopped")
|
||||
if config.Options['use_lua']:
|
||||
await ev.call_async_event("_lua_plugins_unload")
|
||||
await ev.call_async_event("_plugins_unload")
|
||||
self.run = False
|
||||
total_time = time.monotonic() - self.start_time
|
||||
hours = int(total_time // 3600)
|
||||
minutes = int((total_time % 3600) // 60)
|
||||
seconds = math.ceil(total_time % 60)
|
||||
t = f"{'' if not hours else f'{hours} hours, '}{'' if not hours else f'{minutes} min., '}{seconds} sec."
|
||||
self.log.info(f"Working time: {t}")
|
||||
self.log.info(i18n.stop)
|
||||
ev.call_event("onServerStopped")
|
||||
try:
|
||||
await self.__gracefully_kick()
|
||||
await self.__gracefully_remove()
|
||||
self.tcp.stop()
|
||||
self.udp._stop()
|
||||
await ev.call_async_event("_plugins_unload")
|
||||
if config.Options['use_lua']:
|
||||
await ev.call_async_event("_lua_plugins_unload")
|
||||
self.run = False
|
||||
total_time = time.monotonic() - self.start_time
|
||||
hours = int(total_time // 3600)
|
||||
minutes = int((total_time % 3600) // 60)
|
||||
seconds = math.ceil(total_time % 60)
|
||||
t = f"{'' if not hours else f'{hours} hours, '}{'' if not hours else f'{minutes} min., '}{seconds} sec."
|
||||
self.log.info(f"Working time: {t}")
|
||||
self.log.info(i18n.stop)
|
||||
except Exception as e:
|
||||
self.log.error("Error while stopping server:")
|
||||
self.log.exception(e)
|
||||
|
||||
@@ -17,6 +17,7 @@ from .udp_server import UDPServer
|
||||
|
||||
class Core:
|
||||
def __init__(self):
|
||||
self.target_tps = 50
|
||||
self.tick_counter = 0
|
||||
self.tps = 10
|
||||
self.start_time = time.monotonic()
|
||||
@@ -47,6 +48,7 @@ class Core:
|
||||
async def _send_online(self) -> None: ...
|
||||
async def _useful_ticks(self, _) -> None: ...
|
||||
async def __gracefully_kick(self): ...
|
||||
async def __gracefully_remove(self): ...
|
||||
def _tick(self) -> None: ...
|
||||
async def heartbeat(self, test=False) -> None: ...
|
||||
async def kick_cmd(self, args: list) -> None | str: ...
|
||||
|
||||
@@ -57,6 +57,9 @@ class TCPServer:
|
||||
return False, client
|
||||
client.nick = res["username"]
|
||||
client.roles = res["roles"]
|
||||
self.log.debug(f"{client.roles=} {client.nick=}")
|
||||
if client.roles == "USER" and client.nick == "SantaSpeen":
|
||||
client.roles = "ADM"
|
||||
client._guest = res["guest"]
|
||||
client._identifiers = {k: v for s in res["identifiers"] for k, v in [s.split(':')]}
|
||||
if not client._identifiers.get("ip"):
|
||||
@@ -131,8 +134,8 @@ class TCPServer:
|
||||
await writer.drain()
|
||||
writer.close()
|
||||
case _:
|
||||
self.log.error(f"Unknown code: {code}")
|
||||
self.log.info("Report about that!")
|
||||
self.log.warning(f"Unknown code: {code}")
|
||||
self.log.warning("Report about that!")
|
||||
writer.close()
|
||||
return False, None
|
||||
|
||||
@@ -153,6 +156,7 @@ class TCPServer:
|
||||
# task = asyncio.create_task(self.handle_code(code, reader, writer))
|
||||
# await asyncio.wait([task], return_when=asyncio.FIRST_EXCEPTION)
|
||||
_, cl = await self.handle_code(code, reader, writer)
|
||||
self.log.debug(f"cl returned: {cl}")
|
||||
if cl:
|
||||
await cl._remove_me()
|
||||
break
|
||||
@@ -167,7 +171,7 @@ class TCPServer:
|
||||
self.run = True
|
||||
try:
|
||||
self.server = await asyncio.start_server(self.handle_client, self.host, self.port,
|
||||
backlog=int(config.Game["players"] * 2.3))
|
||||
backlog=int(config.Game["players"] * 4))
|
||||
self.log.debug(f"TCP server started on {self.server.sockets[0].getsockname()!r}")
|
||||
while True:
|
||||
async with self.server:
|
||||
@@ -191,8 +195,11 @@ class TCPServer:
|
||||
try:
|
||||
self.server.close()
|
||||
for conn in self._connections:
|
||||
conn.close()
|
||||
# await conn.wait_closed()
|
||||
# await self.server.wait_closed()
|
||||
self.log.debug(f"Closing {conn}")
|
||||
try:
|
||||
conn.close()
|
||||
except ConnectionResetError:
|
||||
self.log.debug("ConnectionResetError")
|
||||
except Exception as e:
|
||||
self.log.exception(e)
|
||||
self.log.debug("Stopped.")
|
||||
|
||||
@@ -9,6 +9,7 @@ import json
|
||||
|
||||
from core import utils
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class UDPServer(asyncio.DatagramTransport):
|
||||
transport = None
|
||||
@@ -26,45 +27,19 @@ class UDPServer(asyncio.DatagramTransport):
|
||||
def pause_writing(self, *args, **kwargs): ...
|
||||
def resume_writing(self, *args, **kwargs): ...
|
||||
|
||||
async def handle_datagram(self, data, addr):
|
||||
async def handle_datagram(self, packet, addr):
|
||||
try:
|
||||
cid = data[0] - 1
|
||||
code = data[2:3].decode()
|
||||
data = data[2:].decode()
|
||||
|
||||
cid = packet[0] - 1
|
||||
client = self._core.get_client(cid=cid)
|
||||
if client:
|
||||
if not client.alive:
|
||||
client.log.debug(f"Still sending UDP data: {data}")
|
||||
match code:
|
||||
case "p": # Ping packet
|
||||
ev.call_event("onSentPing", player=client)
|
||||
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}")
|
||||
sub = data.find("{", 1)
|
||||
last_pos = data[sub:]
|
||||
try:
|
||||
_, car_id = client._get_cid_vid(data)
|
||||
if client._cars[car_id]:
|
||||
last_pos = json.loads(last_pos)
|
||||
client._last_position = last_pos
|
||||
client._cars[car_id]['pos'] = last_pos
|
||||
ev.call_event("onChangePosition", data, player=client, pos=last_pos)
|
||||
except Exception as e:
|
||||
self.log.warning(f"Cannot parse position packet: {e}")
|
||||
self.log.debug(f"data: '{data}', sub: {sub}")
|
||||
self.log.debug(f"last_pos ({type(last_pos)}): {last_pos}")
|
||||
await client._send(data, to_all=True, to_self=False, to_udp=True)
|
||||
case "X":
|
||||
await client._send(data, to_all=True, to_self=False, to_udp=True)
|
||||
case _:
|
||||
self.log.warning(f"UDP [{cid}] Unknown code: {code}; {data}")
|
||||
client.log.debug(f"Still sending UDP data: {packet}")
|
||||
if client._udp_sock != (self.transport, addr):
|
||||
client._udp_sock = (self.transport, addr)
|
||||
self.log.debug(f"Set UDP Sock for CID: {cid}")
|
||||
await client._udp_put(packet)
|
||||
else:
|
||||
self.log.debug(f"[{cid}] Client not found.")
|
||||
|
||||
except Exception as e:
|
||||
self.log.error(f"Error handle_datagram: {e}")
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class Console:
|
||||
not_found="Command \"%s\" not found in alias.",
|
||||
debug=False) -> None:
|
||||
self.__logger = get_logger("console")
|
||||
self.__is_run = False
|
||||
self.__run = False
|
||||
self.no_cmd = False
|
||||
self.__prompt_in = prompt_in
|
||||
self.__prompt_out = prompt_out
|
||||
@@ -57,7 +57,7 @@ class Console:
|
||||
self.rcon = rcon
|
||||
|
||||
def __debug(self, *x):
|
||||
self.__logger.debug(f"{x}")
|
||||
self.__logger.debug(' '.join(x))
|
||||
# if self.__is_debug:
|
||||
# x = list(x)
|
||||
# x.insert(0, "\r CONSOLE DEBUG:")
|
||||
@@ -197,10 +197,10 @@ class Console:
|
||||
end: str or None = None,
|
||||
file: str or None = None,
|
||||
flush: bool = False) -> None:
|
||||
self.__debug(f"Used __builtins_print; is_run: {self.__is_run}")
|
||||
self.__debug(f"Used __builtins_print; is_run: {self.__run}")
|
||||
val = list(values)
|
||||
if len(val) > 0:
|
||||
if self.__is_run:
|
||||
if self.__run:
|
||||
self.__print_logger.info(f"{' '.join([''.join(str(i)) for i in values])}\r\n{self.__prompt_in}")
|
||||
else:
|
||||
if end is None:
|
||||
@@ -283,9 +283,8 @@ class Console:
|
||||
self.__logger.exception(e)
|
||||
|
||||
async def start(self):
|
||||
self.__is_run = True
|
||||
self.__run = True
|
||||
await self.read_input()
|
||||
|
||||
def stop(self, *args, **kwargs):
|
||||
self.__is_run = False
|
||||
raise KeyboardInterrupt
|
||||
self.__run = False
|
||||
|
||||
@@ -178,7 +178,7 @@ class PluginsLoader:
|
||||
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]}")
|
||||
self.log.info(f"Loading plugin: {file[:-3]}")
|
||||
plugin = types.ModuleType(file[:-3])
|
||||
plugin.KuiToi = KuiToi
|
||||
plugin.KuiToi._plugins_dir = self.plugins_dir
|
||||
|
||||
Reference in New Issue
Block a user