【问题标题】:how run a loop without stopping the rest of the script in discord.py如何在不停止 discord.py 中其余脚本的情况下运行循环
【发布时间】:2022-01-19 11:38:20
【问题描述】:

我试图制作一个类似于 Minecraft 服务器控制台的不和谐机器人,但是,我觉得这是一个非常基本的问题,我希望机器人继续发送 Minecraft 服务器的输出并通过消息写入输入一个特定的通道,但当然,如果你有一个循环运行程序的其余部分没有运行,我尝试了多进程和多线程,但由于异步函数它没有工作。 这是我的代码:

minecraft_dir = r"C:\Users\Pablo\Desktop\1.18 server - Copy"
executable = r'java -Xms4G -Xmx4G -jar "C:\Users\Pablo\Desktop\1.18 server - Copy\server.jar" java'
process = None

async def start_serv(msg):
  os.chdir(minecraft_dir)
  process = subprocess.Popen(executable,stdin=PIPE,stdout=PIPE, text=True)
  for line in process.stdout:
    await msg.channel.send(line)

def serv_cmd(cmd):
  if cmd == "stop":
    process = None
  cmd = cmd + "\n"
  cmd = cmd.encode()
  process.stdin.write(cmd)
  process.stding.flush()

@client.event
async def on_message(message):
  global process
  if message.author.id != client.user.id and message.channel.name == "mc-server-console":
    command = message.content
    command=command.lower()
    if command == "start":
      if process == None:
        await message.channel.send("yessir")
    if process != None:
      serv_cmd(command)

这是我的多线程尝试:

async def start_serv():
  os.chdir(minecraft_dir)
  process = subprocess.Popen(executable,stdin=PIPE,stdout=PIPE, text=True)
  for line in process.stdout:
    await channel.send(line)
    time.sleep(0.1)

def serv_cmd(cmd):
  if cmd == "stop":
    process = None
  cmd = cmd + "\n"
  cmd = cmd.encode()
  process.stdin.write(cmd)
  process.stding.flush()

def main():
  @client.event
  async def on_message(message):
    global process
    if message.author.id != client.user.id and message.channel.name == "mc-server-console":
      command = message.content
      command=command.lower()
      if command == "start":
        if process == None:
          await message.channel.send("yessir")
          thread2.start()
      if process != None:
        serv_cmd(command)

thread1 = threading.Thread(target=main)
thread1.start()
thread2 = threading.Thread(target=start_serv)

如果有人知道只在服务器输出更新时发送消息的方法,我认为这也可以。

【问题讨论】:

  • 你能展示你在多处理和多线程方面的尝试吗?
  • @jakub 你去我找不到我的多处理尝试,但我不认为它有很大不同。
  • 为此,您只需在每次运行脚本时更新您的机器人。您还可以创建一个 cog 来执行此操作,它不应停止任何其他脚本。
  • @EpicEfeathers 我将如何更新脚本?
  • 老实说,我只是创建一个 cog 并在其中放置您的循环,因为它不会影响您的其他文件。

标签: python discord.py stdout minecraft


【解决方案1】:

线程实际上在这里并不是一个好的举措。大多数时候discord.py 的线程不会是“线程安全的”。你可以弄乱Lock's,但尽可能避免它们更容易。请注意这是一个原型,因为我没有测试过任何一个(我没有 MC)。我会把它当作一个概念,用它来指导你

让我们介绍一种在不阻塞事件循环的情况下做你想做的事的方法。

import os
import subprocess
import aiohttp
import discord
from discord.ext import commands
from typing import TYPE_CHECKING, Generic, TypeVar, Any

if TYPE_CHECKING:
    from asyncio import AbstractEventLoop
    
B = TypeVar('B', bound='commands.Bot')

# We'll make a class caleld MinecraftRunner
# to hold all minecraft based operations
    
class MinecraftRunner(Generic[B]):
    def __init__(self, bot: B) -> None:
        self.bot: commands.Bot = bot
        self.loop: AbstractEventLoop = bot.loop
        self.session: aiohttp.ClientSession = getattr(bot, 'session', aiohttp.ClientSession())
        
        self.minecraft_dir: str = r"C:\Users\Pablo\Desktop\1.18 server - Copy"
        self.executable: str = r'java -Xms4G -Xmx4G -jar "C:\Users\Pablo\Desktop\1.18 server - Copy\server.jar" java'
        
        # We'll use webhooks to send to the channel instead of `channel.send`. 
        # This will not rate-limit our bot.
        self.webhook_url: str = '' 
        
        # Using loop.create_task will allow us to not block
        # the main event loop.
        self.runner_task = self.loop.create_task(self.main_loop())
        
    async def send_to_webhook(self, *args, **kwargs) -> discord.WebhookMessage:
        if not (webhook := getattr(self, 'webhook', None)):
            self.webhook = webhook = await (discord.Webhook.from_url(self.webhook_url, session=self.session, bot_token=self.bot.http.token)).fetch()

        return await webhook.send(*args, **kwargs)
    
    async def main_loop(self) -> None:
        await self.bot.wait_until_ready() # Don't do anything until our bot is ready to do work.
        
        os.chdir(self.minecraft_dir)
        self.process = process = subprocess.Popen(self.executable,stdin=subprocess.PIPE,stdout=subprocess.PIPE, text=True)
        
        for line in process.stdout:
            await self.send_to_webhook(line)
    
# Let's put our on_message into a simple cog as well
class MinecraftListener(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot: commands.Bot = bot
        self.minecraft_runner = MinecraftRunner(bot)
        
    def cog_unload(self) -> None:
        self.minecraft_runner.runner_task.cancel()
        
        try:
            delattr(self.minecraft_runner, 'runner_task')
        except AttributeError:
            pass
        
    async def send_command(self, command: str) -> None:
        command += '\n'
        command.encode()
        self.minecraft_runner.process.stdin.write(command)
        self.minecraft_runner.process.stdin.flush()
    
    @commands.Cog.listener()
    async def on_message(self, message: discord.Message) -> Any:
        if message.author.bot: # This message was sent from a bot.
            return
        if message.webhook_id: # This message is a webhook message.
            return
        
        command = message.content.lower()
        channel = message.channel
        
        if command == 'start':
            if not hasattr(self.minecraft_runner, 'runner_task'):
                self.minecraft_runner.runner_task = self.minecraft_runner.loop.create_task(self.minecraft_runner.main_loop())
                return await channel.send('The process has been started.')
            
            return await channel.send('The process is already started.')
                
        elif command == 'stop': # Stop the process
            if not hasattr(self.minecraft_runner, 'runner_task'):
                return await channel.send('The process is not running!')
            
            return self.cog_unload()
        
        # This isn't a custom command, send it to the minecraft process
        await self.send_command(command)

【讨论】:

  • 感谢您回答我的问题,也很抱歉花了这么长时间才离开我的电脑。正如您可能已经从我糟糕的代码中看出的那样,我对编程很陌生,我不得不花一个小时阅读有关此代码的文档以了解一半的情况,老实说,我仍然不明白这是怎么回事会起作用,以及为什么你创建了一个类和 cog。我想知道你为什么要做这一切而不仅仅是复制所以如果你能向我解释一下我会很高兴
  • 如果你觉得不和谐请加我,我可以帮助你理解它 Chai#9762
  • 听起来不错,我是蘑菇
  • 不想听起来很需要但你能接受我的不和谐吗?
猜你喜欢
  • 2011-07-10
  • 1970-01-01
  • 2021-09-15
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 2016-09-18
  • 1970-01-01
相关资源
最近更新 更多