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 uuid
from asyncio import Task
from asyncio import Task, StreamReader, StreamWriter, Server
from enum import Enum
from typing import Dict, List, Callable, Type, Optional, Tuple, AsyncIterator
@ -29,6 +29,7 @@ class Server:
_dispatcher_pool : List[Dispatcher]
_processing : bool
_server : Server
_worker : Task
_callbacks = Dict[str, Task]
@ -44,15 +45,11 @@ class Server:
self.port = port
self.options = options or {
"reconnect" : True,
"rctime" : 5.0,
"keep-alive" : True,
"poll-timeout" : 1,
"online-mode" : False,
}
self._dispatcher_pool = []
self._processing = False
self._authenticated = False
self._logger = LOGGER.getChild(f"@({self.host}:{self.port})")
@ -79,49 +76,108 @@ class Server:
self._logger.info("Received SIGINT, stopping for real")
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._worker = asyncio.get_event_loop().create_task(self._server_worker())
self._logger.info("Minecraft server started")
async with self._server:
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):
self._processing = False
if self.dispatcher.connected:
await self.dispatcher.disconnect(block=block)
self._server.close()
if block:
await self._worker
self._logger.info("Minecraft server stopped")
if block and wait_tasks:
await asyncio.gather(*list(self._callbacks.values()))
await self._server.wait_closed()
# if block and wait_tasks: # TODO wait for client workers
# await asyncio.gather(*list(self._callbacks.values()))
async def _server_worker(self):
while self._processing:
try:
await self.authenticate()
except AuthException as e:
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 _disconnect_client(self, dispatcher):
if dispatcher.state == ConnectionState.LOGIN:
await dispatcher.write(PacketDisconnect(dispatcher.proto, reason="Connection terminated"))
else:
await dispatcher.write(PacketKickDisconnect(dispatcher.proto, reason="Connection terminated"))
async def _handshake(self) -> bool: # TODO make this fancier! poll for version and status first
pass
async def _server_worker(self, reader:StreamReader, writer:StreamWriter):
dispatcher = Dispatcher()
async def _login(self) -> bool:
pass
self._logger.debug("Starting dispatcher for client")
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