some work on server impl

This commit is contained in:
əlemi 2021-11-21 03:54:09 +01:00
parent f35d927444
commit 741ec33f1b

View file

@ -2,7 +2,7 @@ import asyncio
import logging import logging
import uuid import uuid
from asyncio import Task from asyncio import Task, StreamReader, StreamWriter, Server
from enum import Enum from enum import Enum
from typing import Dict, List, Callable, Type, Optional, Tuple, AsyncIterator from typing import Dict, List, Callable, Type, Optional, Tuple, AsyncIterator
@ -29,6 +29,7 @@ class Server:
_dispatcher_pool : List[Dispatcher] _dispatcher_pool : List[Dispatcher]
_processing : bool _processing : bool
_server : Server
_worker : Task _worker : Task
_callbacks = Dict[str, Task] _callbacks = Dict[str, Task]
@ -44,15 +45,11 @@ class Server:
self.port = port self.port = port
self.options = options or { self.options = options or {
"reconnect" : True, "online-mode" : False,
"rctime" : 5.0,
"keep-alive" : True,
"poll-timeout" : 1,
} }
self._dispatcher_pool = [] self._dispatcher_pool = []
self._processing = False self._processing = False
self._authenticated = False
self._logger = LOGGER.getChild(f"@({self.host}:{self.port})") self._logger = LOGGER.getChild(f"@({self.host}:{self.port})")
@ -79,49 +76,108 @@ class Server:
self._logger.info("Received SIGINT, stopping for real") self._logger.info("Received SIGINT, stopping for real")
loop.run_until_complete(self.stop(wait_tasks=False)) loop.run_until_complete(self.stop(wait_tasks=False))
async def start(self): async def start(self, block=False):
self._server = await asyncio.start_server(
self._server_worker, self.host, self.port
)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addrs}')
self._processing = True self._processing = True
self._worker = asyncio.get_event_loop().create_task(self._server_worker()) async with self._server:
self._logger.info("Minecraft server started") self._logger.info("Minecraft server started")
if block:
await self._server.serve_forever()
else:
await self._server.start_serving()
async def stop(self, block=True, wait_tasks=True): async def stop(self, block=True, wait_tasks=True):
self._processing = False self._processing = False
if self.dispatcher.connected: if self.dispatcher.connected:
await self.dispatcher.disconnect(block=block) await self.dispatcher.disconnect(block=block)
self._server.close()
if block: if block:
await self._worker await self._server.wait_closed()
self._logger.info("Minecraft server stopped") # if block and wait_tasks: # TODO wait for client workers
if block and wait_tasks: # await asyncio.gather(*list(self._callbacks.values()))
await asyncio.gather(*list(self._callbacks.values()))
async def _server_worker(self): async def _disconnect_client(self, dispatcher):
while self._processing: if dispatcher.state == ConnectionState.LOGIN:
try: await dispatcher.write(PacketDisconnect(dispatcher.proto, reason="Connection terminated"))
await self.authenticate() else:
except AuthException as e: await dispatcher.write(PacketKickDisconnect(dispatcher.proto, reason="Connection terminated"))
self._logger.error(str(e))
break
try:
await self.dispatcher.connect(self.host, self.port)
await self._handshake()
if await self._login():
await self._play()
except ConnectionRefusedError:
self._logger.error("Server rejected connection")
except Exception:
self._logger.exception("Exception in Client connection")
if self.dispatcher.connected:
await self.dispatcher.disconnect()
if not self.options["reconnect"]:
break
await asyncio.sleep(self.options["rctime"])
await self.stop(block=False)
async def _handshake(self) -> bool: # TODO make this fancier! poll for version and status first async def _server_worker(self, reader:StreamReader, writer:StreamWriter):
pass dispatcher = Dispatcher()
async def _login(self) -> bool: self._logger.debug("Starting dispatcher for client")
pass await dispatcher.connect(
host=self.host,
port=self.port,
reader=reader,
writer=writer,
)
await self._handshake(dispatcher)
if dispatcher.state == ConnectionState.STATUS:
await self._status(dispatcher)
elif dispatcher.state == ConnectionState.LOGIN:
if await self._login(dispatcher):
await self._play(dispatcher)
if dispatcher.connected:
await self._disconnect_client(dispatcher)
await dispatcher.disconnect()
async def _handshake(self, dispatcher:Dispatcher) -> bool: # TODO make this fancier! poll for version and status first
async for packet in dispatcher.packets():
if isinstance(packet, PacketSetProtocol):
dispatcher.proto = packet.protocolVersion
if packet.nextState == ConnectionState.STATUS:
dispatcher.state = ConnectionState.STATUS
return True
elif packet.nextState == ConnectionState.LOGIN:
dispatcher.state = ConnectionState.LOGIN
return True
return False
async def _status(self, dispatcher:Dispatcher) -> bool:
async for packet in dispatcher.packets():
pass # TODO handle status!
return False
async def _login(self, dispatcher:Dispatcher) -> bool:
async for packet in dispatcher.packets():
if isinstance(packet, PacketLoginStart):
if self.options["online-mode"]:
# await dispatcher.write(
# PacketEncryptionBegin(
# dispatcher.proto,
# serverId="????",
# publicKey=b"??????",
# verifyToken=b"1234",
# )
# )
pass
else:
await dispatcher.write(
PacketSuccess(
dispatcher.proto,
uuid=packet.username,
username=packet.username,
)
)
return True
elif isinstance(packet, PacketEncryptionResponse):
shared_secret = packet.sharedSecret
verify_token = packet.verifyToken
# TODO enable encryption?
# return True
return False
async def _play(self, dispatcher:Dispatcher) -> bool:
async for packet in dispatcher.packets():
pass # TODO handle play
return False
async def _play(self) -> bool:
pass