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 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
|
|
||||||
|
|
Loading…
Reference in a new issue