some work on server impl
This commit is contained in:
parent
f35d927444
commit
741ec33f1b
1 changed files with 97 additions and 41 deletions
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue