You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Discord机器人多服务器游戏状态独立运行

故障原因

你的代码存在5个核心问题,导致多服务器状态隔离完全失效:

  • 没有删除全局ingame标记,这个标记是所有服务器共享的,只要任意一个服务器开启游戏修改了这个值,其余服务器的启动请求都会被拦截,你写的服务器状态字典完全没有参与核心逻辑判断。
  • on_guild_join事件函数没有定义guild入参,机器人加入新服务器时根本拿不到对应的服务器实例,初始化状态字典的逻辑根本不会正常执行。
  • 字典键类型不统一:初始化的时候用guild.name(字符串类型的服务器名)当键,命令判断的时候又用ctx.guild(Discord的Guild实例对象)当键,两种类型永远无法匹配,键存在性判断完全失效。
  • 状态值逻辑写反:给不存在的服务器初始化状态时直接设为True,和“游戏中”的状态值重合,初始无游戏的状态应该为False。
  • 提示语的字符串占位符没有传入对应参数,发送消息时会直接报格式化错误。
修复方案

不要用全局变量存游戏状态,统一用服务器唯一ID作为字典键做状态隔离,参考实现如下:

import discord
import random
from discord.ext import commands

client = commands.Bot(command_prefix="!")  # 替换成你自己的命令前缀
# 用服务器ID作为键存储游戏状态,值为True代表对应服务器有正在进行的游戏,False代表无游戏
games_in_progress = {}
# 存储游戏对应答案、所在频道,方便逻辑判断和提示引用
game_data = {}

@client.event
async def on_ready():
    # 机器人启动时初始化所有已加入服务器的状态,避免重启后状态丢失
    for guild in client.guilds:
        games_in_progress[guild.id] = False
    print(f"已登录为{client.user},共加载{len(client.guilds)}个服务器的状态")

@client.event
async def on_guild_join(guild):
    # 新加入服务器时初始化状态
    games_in_progress[guild.id] = False
    print(f"已加入服务器{guild.name},已初始化游戏状态")

@client.command()
async def startgame(ctx, min_num:int=1, max_num:int=100):
    guild_id = ctx.guild.id
    # 先判断当前服务器有没有正在进行的游戏
    if games_in_progress.get(guild_id, False):
        await ctx.send(f"当前服务器已有正在进行的游戏,请先结束{game_data[guild_id]['channel'].mention}的游戏再开启新局")
        return
    # 校验数字区间合法性
    if min_num >= max_num:
        await ctx.send("数字区间不合法,最小值必须小于最大值")
        return
    # 生成随机答案,标记当前服务器进入游戏中状态
    answer = random.randint(min_num, max_num)
    games_in_progress[guild_id] = True
    game_data[guild_id] = {
        "answer": answer,
        "min": min_num,
        "max": max_num,
        "channel": ctx.channel
    }
    await ctx.send(f"猜数字游戏已启动!请猜{min_num}到{max_num}之间的整数")

@client.command()
async def guess(ctx, num:int):
    guild_id = ctx.guild.id
    if not games_in_progress.get(guild_id, False):
        await ctx.send("当前服务器没有正在进行的猜数字游戏,请先使用startgame命令开局")
        return
    target = game_data[guild_id]["answer"]
    if num == target:
        await ctx.send(f"恭喜你猜对了!答案就是{target},本局游戏结束")
        # 猜对后重置状态
        games_in_progress[guild_id] = False
        del game_data[guild_id]
    elif num < target:
        await ctx.send("猜小了,再试试更大的数字")
    else:
        await ctx.send("猜大了,再试试更小的数字")
注意事项
  • 不要用服务器名作为状态键,服务器名称可以被管理员随意修改,会导致状态匹配失效,Discord提供的guild.id是全局唯一且永久不变的,最适合做状态键。
  • 所有游戏结束的分支(玩家猜对答案、超时自动结束、管理员手动终止)都必须重置对应服务器的games_in_progress状态,否则服务器会一直被判定为有游戏在进行。

内容的提问来源于stack exchange,提问作者Moby772

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:57:23