changed idea on dispatcher, some server scaffolding

This commit is contained in:
əlemi 2021-11-20 16:26:24 +01:00 committed by alemidev
parent a5b34b4d78
commit f35d927444
2 changed files with 136 additions and 24 deletions

View file

@ -106,6 +106,8 @@ class Dispatcher:
async def connect(self,
host : Optional[str] = None,
port : Optional[int] = None,
reader : Optional[StreamReader] = None,
writer : Optional[StreamWriter] = None,
queue_timeout : int = 1,
queue_size : int = 100
):
@ -114,6 +116,9 @@ class Dispatcher:
self._prepare(host, port, queue_timeout, queue_size)
if reader and writer:
self._down, self._up = reader, writer
else:
self._down, self._up = await asyncio.open_connection(
host=self._host,
port=self._port,
@ -124,26 +129,6 @@ class Dispatcher:
self._writer = asyncio.get_event_loop().create_task(self._up_worker(timeout=queue_timeout))
self._logger.info("Connected")
@classmethod
def serve(cls,
container : List[Dispatcher],
host : Optional[str] = None,
port : Optional[int] = None,
queue_timeout : int = 1,
queue_size : int = 100
):
async def _client_connected(reader:StreamReader, writer:StreamWriter):
dispatcher = cls()
container.append(dispatcher)
dispatcher._prepare(host, port, queue_timeout, queue_size)
dispatcher._down, dispatcher._up = reader, writer
dispatcher._dispatching = True
dispatcher._reader = asyncio.get_event_loop().create_task(dispatcher._down_worker())
dispatcher._writer = asyncio.get_event_loop().create_task(dispatcher._up_worker(timeout=queue_timeout))
dispatcher._logger.info("Serving client")
return _client_connected
async def disconnect(self, block:bool=True):
self._dispatching = False
if block and self._writer and self._reader:

127
aiocraft/server.py Normal file
View file

@ -0,0 +1,127 @@
import asyncio
import logging
import uuid
from asyncio import Task
from enum import Enum
from typing import Dict, List, Callable, Type, Optional, Tuple, AsyncIterator
from .dispatcher import Dispatcher
from .mc.packet import Packet
from .mc.token import Token, AuthException
from .mc.definitions import Dimension, Difficulty, Gamemode, ConnectionState
from .mc.proto.handshaking.serverbound import PacketSetProtocol
from .mc.proto.play.serverbound import PacketKeepAlive as PacketKeepAliveResponse
from .mc.proto.play.clientbound import PacketKeepAlive, PacketSetCompression, PacketKickDisconnect
from .mc.proto.login.serverbound import PacketLoginStart, PacketEncryptionBegin as PacketEncryptionResponse
from .mc.proto.login.clientbound import (
PacketCompress, PacketDisconnect, PacketEncryptionBegin, PacketLoginPluginRequest, PacketSuccess
)
from .util import encryption
LOGGER = logging.getLogger(__name__)
class Server:
host:str
port:int
options:dict
_dispatcher_pool : List[Dispatcher]
_processing : bool
_worker : Task
_callbacks = Dict[str, Task]
_logger : logging.Logger
def __init__(
self,
host:str,
port:int = 25565,
options:dict = None,
):
self.host = host
self.port = port
self.options = options or {
"reconnect" : True,
"rctime" : 5.0,
"keep-alive" : True,
"poll-timeout" : 1,
}
self._dispatcher_pool = []
self._processing = False
self._authenticated = False
self._logger = LOGGER.getChild(f"@({self.host}:{self.port})")
@property
def started(self) -> bool:
return self._processing
def run(self):
loop = asyncio.get_event_loop()
loop.run_until_complete(self.start())
async def idle():
while self._processing: # TODO don't busywait even if it doesn't matter much
await asyncio.sleep(self.options["poll-timeout"])
try:
loop.run_forever(idle())
except KeyboardInterrupt:
self._logger.info("Received SIGINT, stopping...")
try:
loop.run_until_complete(self.stop())
except KeyboardInterrupt:
self._logger.info("Received SIGINT, stopping for real")
loop.run_until_complete(self.stop(wait_tasks=False))
async def start(self):
self._processing = True
self._worker = asyncio.get_event_loop().create_task(self._server_worker())
self._logger.info("Minecraft server started")
async def stop(self, block=True, wait_tasks=True):
self._processing = False
if self.dispatcher.connected:
await self.dispatcher.disconnect(block=block)
if block:
await self._worker
self._logger.info("Minecraft server stopped")
if block and wait_tasks:
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 _handshake(self) -> bool: # TODO make this fancier! poll for version and status first
pass
async def _login(self) -> bool:
pass
async def _play(self) -> bool:
pass