changed idea on dispatcher, some server scaffolding
This commit is contained in:
parent
a5b34b4d78
commit
f35d927444
2 changed files with 136 additions and 24 deletions
|
@ -106,6 +106,8 @@ class Dispatcher:
|
||||||
async def connect(self,
|
async def connect(self,
|
||||||
host : Optional[str] = None,
|
host : Optional[str] = None,
|
||||||
port : Optional[int] = None,
|
port : Optional[int] = None,
|
||||||
|
reader : Optional[StreamReader] = None,
|
||||||
|
writer : Optional[StreamWriter] = None,
|
||||||
queue_timeout : int = 1,
|
queue_timeout : int = 1,
|
||||||
queue_size : int = 100
|
queue_size : int = 100
|
||||||
):
|
):
|
||||||
|
@ -114,6 +116,9 @@ class Dispatcher:
|
||||||
|
|
||||||
self._prepare(host, port, queue_timeout, queue_size)
|
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(
|
self._down, self._up = await asyncio.open_connection(
|
||||||
host=self._host,
|
host=self._host,
|
||||||
port=self._port,
|
port=self._port,
|
||||||
|
@ -124,26 +129,6 @@ class Dispatcher:
|
||||||
self._writer = asyncio.get_event_loop().create_task(self._up_worker(timeout=queue_timeout))
|
self._writer = asyncio.get_event_loop().create_task(self._up_worker(timeout=queue_timeout))
|
||||||
self._logger.info("Connected")
|
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):
|
async def disconnect(self, block:bool=True):
|
||||||
self._dispatching = False
|
self._dispatching = False
|
||||||
if block and self._writer and self._reader:
|
if block and self._writer and self._reader:
|
||||||
|
|
127
aiocraft/server.py
Normal file
127
aiocraft/server.py
Normal 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
|
Loading…
Reference in a new issue