How to start async target inside thread

Viewed 122

I'm using the pyCraft library to make a bridge with discord and minecraft and I've been trying multiple solutions like the asyncio loop thing but when ran it just runs the minecraft client and doesn't start the discord client until I do a keyboard interrupt. The only way I can figure out how this could work is with threading. This function needs to be async because it uses the await ctx.send method to send to a channel. I also need to have discord and the function be able to interact with each other. Asyncio:

import discord
import asyncio
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound

client = discord.Client()

async def mcClient(server='mc.hypixel.net', username='minecraft email', password='password', *, port=25565, version=340):
    # version 340 - Minecraft 1.12.2
    if password is None:
        print("Connecting in offline mode...")
        connection = Connection(
            server, port, username=username)
    else:
        auth_token = authentication.AuthenticationToken()
        try:
            auth_token.authenticate(username, password)
        except YggdrasilError as e:
            print(e)
            sys.exit()
        print("(Online) Logged in as %s..." % auth_token.username)
        connection = Connection(
            server, port, auth_token=auth_token, initial_version=version)

    # listener on join game
    def handle_join_game(join_game_packet):
        print('Connected.')
    
    connection.register_packet_listener(
        handle_join_game, clientbound.play.JoinGamePacket)
    
    # print chat
    def handle_chat(chat_packet):
        cpke = chat_packet.json_data
        print(f"Chat: {cpke}")
        channel = client.get_channel(channel_id)
        await channel.send(cpke)

    connection.register_packet_listener(
        handle_chat, clientbound.play.ChatMessagePacket)

    connection.connect()

    # listen for input and send message
    while True:
        try:
            text = input()
            packet = serverbound.play.ChatPacket()
            packet.message = text
            connection.write_packet(packet)
        except KeyboardInterrupt:
            print("Exiting client.")
            sys.exit()

asyncio.get_event_loop().create_task(mcClient())
client.run(TOKEN)

Threading (without async):

import discord
import threading
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound

client = discord.Client()

def mcClient(server='mc.hypixel.net', username='minecraft email', password='password', *, port=25565, version=340):
    # version 340 - Minecraft 1.12.2
    if password is None:
        print("Connecting in offline mode...")
        connection = Connection(
            server, port, username=username)
    else:
        auth_token = authentication.AuthenticationToken()
        try:
            auth_token.authenticate(username, password)
        except YggdrasilError as e:
            print(e)
            sys.exit()
        print("(Online) Logged in as %s..." % auth_token.username)
        connection = Connection(
            server, port, auth_token=auth_token, initial_version=version)

    # listener on join game
    def handle_join_game(join_game_packet):
        print('Connected.')
    
    connection.register_packet_listener(
        handle_join_game, clientbound.play.JoinGamePacket)
    
    # print chat
    def handle_chat(chat_packet):
        cpke = chat_packet.json_data
        print(f"Chat: {cpke}")

    connection.register_packet_listener(
        handle_chat, clientbound.play.ChatMessagePacket)

    connection.connect()

    # listen for input and send message
    while True:
        try:
            text = input()
            packet = serverbound.play.ChatPacket()
            packet.message = text
            connection.write_packet(packet)
        except KeyboardInterrupt:
            print("Exiting client.")
            sys.exit()

x = threading.Thread(target=mcClient(), daemon=True)
x.start()
client.run(TOKEN)

While this works, I can't send messages or do anything discord-related. Any help would be appreciated!

Edit: I put the whole function and subfunctions to make it easier to understand

1 Answers

asyncio.gather might help.

import discord
import asyncio
import pyCraft

client = discord.Client()

async def mcClient():
  # stuff
  global connection
  connection = pyCraft.connection(args)
  connection.connect()

async def handle_chat():
  # send message to discord here
  # because of the global statement, you can reference connection from here too!
  pass

asyncio.gather(mcClient(), handle_chat())
client.run(TOKEN)
Related