mirror of
https://github.com/Shu-Ying/main_plugin_farm.git
synced 2026-10-28 05:00:01 +08:00
🐛解决退出时数据库重复关闭导致卡进程的BUG
This commit is contained in:
@@ -57,6 +57,13 @@ __plugin_meta__ = PluginMetadata(
|
||||
help="我的农场返回图片的清晰度, [low, medium, hight, original]",
|
||||
default_value="low",
|
||||
),
|
||||
RegisterConfig(
|
||||
key="动态背景",
|
||||
value=True,
|
||||
help="是否开启根据季节和节日变化的动态背景",
|
||||
type=bool,
|
||||
default_value=True,
|
||||
),
|
||||
RegisterConfig(
|
||||
key="签到图片样式",
|
||||
value="text",
|
||||
|
||||
@@ -35,10 +35,10 @@ class CSqlManager(ABC):
|
||||
if cls.m_pDB is None:
|
||||
cls.m_pDB = await aiosqlite.connect(config.g_sDBFilePath)
|
||||
cls.m_pDB.row_factory = aiosqlite.Row
|
||||
logger.info("真寻农场数据库连接初始化成功")
|
||||
logger.debug("真寻农场数据库连接初始化成功")
|
||||
return True
|
||||
else:
|
||||
logger.info("真寻农场数据库连接已存在,跳过初始化")
|
||||
logger.debug("真寻农场数据库连接已存在,跳过初始化")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"真寻农场数据库连接初始化失败: {e}")
|
||||
@@ -46,10 +46,33 @@ class CSqlManager(ABC):
|
||||
|
||||
@classmethod
|
||||
async def cleanup(cls):
|
||||
if cls.m_pDB:
|
||||
await cls.m_pDB.close()
|
||||
try:
|
||||
if not cls.isConnectionValid():
|
||||
logger.warning("真寻农场数据库连接已关闭或无效,跳过清理")
|
||||
return
|
||||
|
||||
if hasattr(cls, "m_pDB") and cls.m_pDB:
|
||||
await cls.m_pDB.close()
|
||||
cls.m_pDB = None
|
||||
cls._initialized = False
|
||||
logger.debug("真寻农场数据库连接已安全关闭")
|
||||
else:
|
||||
logger.debug("真寻农场数据库连接已不存在,无需关闭")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"真寻农场数据库清理过程中出错: {e}")
|
||||
cls.m_pDB = None
|
||||
logger.info("真寻农场数据库连接已关闭")
|
||||
cls._initialized = False
|
||||
|
||||
@classmethod
|
||||
def isConnectionValid(cls) -> bool:
|
||||
if not hasattr(cls, "m_pDB") or cls.m_pDB is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
return hasattr(cls.m_pDB, "_conn") and cls.m_pDB._conn is not None
|
||||
except:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def getDB(cls) -> aiosqlite.Connection:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,3 +1,6 @@
|
||||
from . import utils
|
||||
|
||||
|
||||
def getConfig():
|
||||
from . import config
|
||||
|
||||
@@ -34,4 +37,5 @@ __all__ = [
|
||||
"getJsonManager",
|
||||
"getRequestManager",
|
||||
"getToolManager",
|
||||
"utils",
|
||||
]
|
||||
|
||||
+88
-23
@@ -4,8 +4,9 @@ from zhenxun.utils._build_image import BuildImage
|
||||
from zhenxun.utils.image_utils import ImageTemplate
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ...core import CPlayer
|
||||
from ...core.dbService import g_pDBService
|
||||
from .. import config, getJsonManager, getToolManager
|
||||
from .. import config, getJsonManager, getToolManager, utils
|
||||
|
||||
|
||||
class CDarwFarm:
|
||||
@@ -19,7 +20,9 @@ class CDarwFarm:
|
||||
Returns:
|
||||
bytes: 返回绘制结果
|
||||
"""
|
||||
img, soilSize, soilPos, grass = await cls.createBaseCanvas()
|
||||
dynamic = Config.get_config("zhenxun_plugin_farm", "动态背景")
|
||||
|
||||
img, soilSize, soilPos, grass = await cls.createBaseCanvas(dynamic)
|
||||
|
||||
player = await getToolManager().getPlayerByUid(uid)
|
||||
if not player:
|
||||
@@ -48,20 +51,44 @@ class CDarwFarm:
|
||||
isFirstExpansion = False
|
||||
|
||||
await cls.drawUserInfo(img, player, uid)
|
||||
|
||||
await cls.applyDefinitionResize(img)
|
||||
|
||||
return img.pic2bytes()
|
||||
|
||||
@classmethod
|
||||
async def createBaseCanvas(cls) -> tuple[BuildImage, tuple, dict, BuildImage]:
|
||||
async def createBaseCanvas(
|
||||
cls, dynamic: bool
|
||||
) -> tuple[BuildImage, tuple, dict, BuildImage]:
|
||||
"""创建基础画布并返回 (img, soilSize, soilPos, grass)
|
||||
|
||||
Returns:
|
||||
tuple: (主画布, 土地尺寸, 土地坐标映射, 草地图)
|
||||
"""
|
||||
img = BuildImage(
|
||||
background=config.g_sResourcePath / "background/background.jpg"
|
||||
)
|
||||
|
||||
if dynamic:
|
||||
season = getToolManager().getSeason()
|
||||
|
||||
backgroundPath = config.g_sResourcePath / "background/decorateIcon"
|
||||
|
||||
# 节日优先级更高
|
||||
if season == utils.Season.SPRING:
|
||||
bgFile = backgroundPath / "decorate_spring_1.jpg"
|
||||
elif season == utils.Season.SUMMER:
|
||||
bgFile = backgroundPath / "decorate_summer_1.jpg"
|
||||
elif season == utils.Season.AUTUMN:
|
||||
bgFile = backgroundPath / "decorate_autumn_1.jpg"
|
||||
elif season == utils.Season.WINTER:
|
||||
bgFile = backgroundPath / "decorate_winter_1.jpg"
|
||||
else:
|
||||
bgFile = backgroundPath / "decorate_default_1.jpg"
|
||||
|
||||
img = BuildImage(background=bgFile)
|
||||
else:
|
||||
img = BuildImage(
|
||||
background=config.g_sResourcePath
|
||||
/ "background/decorateIcon/decorate_default_1jpg"
|
||||
)
|
||||
|
||||
soil = await getJsonManager().getSoil()
|
||||
soilSize = soil["size"]
|
||||
@@ -76,17 +103,17 @@ class CDarwFarm:
|
||||
async def drawUnlockedTile(
|
||||
cls, img: BuildImage, uid: str, soilIndex: int, x: int, y: int, soilSize
|
||||
) -> bool:
|
||||
"""绘制已解锁土地格(包含土地贴图与作物),返回该格是否可收获。
|
||||
"""绘制已解锁土地格(包含土地贴图与作物),返回该格是否可收获
|
||||
|
||||
Args:
|
||||
img (BuildImage): 主画布。
|
||||
uid (str): 用户UID。
|
||||
soilIndex (int): 土地索引(从1开始)。
|
||||
x (int), y (int): 贴图位置。
|
||||
soilSize: 土地贴图尺寸([w,h] 或 (w,h))。
|
||||
img (BuildImage): 主画布
|
||||
uid (str): 用户UID
|
||||
soilIndex (int): 土地索引(从1开始)
|
||||
x (int), y (int): 贴图位置
|
||||
soilSize: 土地贴图尺寸([w,h] 或 (w,h))
|
||||
|
||||
Returns:
|
||||
bool: 是否有可收获作物(isRipe)。
|
||||
bool: 是否有可收获作物(isRipe)
|
||||
"""
|
||||
soilUrl = "soil/普通土地.png"
|
||||
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||
@@ -128,10 +155,10 @@ class CDarwFarm:
|
||||
grass: BuildImage,
|
||||
isFirstExpansion: bool,
|
||||
) -> bool:
|
||||
"""绘制未解锁格子并在首次出现时绘制扩建图标。
|
||||
"""绘制未解锁格子并在首次出现时绘制扩建图标
|
||||
|
||||
Returns:
|
||||
bool: 是否在此格绘制了扩建图标(仅首次返回 True)。
|
||||
bool: 是否在此格绘制了扩建图标(仅首次返回 True)
|
||||
"""
|
||||
await img.paste(grass, (x, y))
|
||||
|
||||
@@ -153,7 +180,7 @@ class CDarwFarm:
|
||||
|
||||
@classmethod
|
||||
async def pasteRipeOnce(cls, img: BuildImage, x: int, y: int, soilSize):
|
||||
"""首次发现成熟作物时贴上成熟提示图(ripe)。"""
|
||||
"""首次发现成熟作物时贴上成熟提示图(ripe)"""
|
||||
ripe = BuildImage(background=config.g_sResourcePath / "background/ripe.png")
|
||||
await img.paste(
|
||||
ripe,
|
||||
@@ -161,22 +188,32 @@ class CDarwFarm:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def drawUserInfo(cls, img: BuildImage, player, uid: str):
|
||||
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)。
|
||||
async def drawUserInfo(cls, img: BuildImage, player: CPlayer, uid: str):
|
||||
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)
|
||||
TODO 需要详细的绘制头像框
|
||||
|
||||
Args:
|
||||
img (BuildImage): 主画布。
|
||||
player: 用户对象(包含 user 字段)。
|
||||
uid (str): 用户UID。
|
||||
img (BuildImage): 主画布
|
||||
player(CPlayer): 用户
|
||||
uid (str): 用户Uid
|
||||
"""
|
||||
# 背景板
|
||||
background = BuildImage(
|
||||
background=config.g_sResourcePath / "background/userInfo.png"
|
||||
)
|
||||
await img.paste(background, (150, 30))
|
||||
|
||||
# 头像
|
||||
image = await PlatformUtils.get_user_avatar(uid, "qq")
|
||||
if image:
|
||||
avatar = BuildImage(background=image)
|
||||
await avatar.resize(0, 140, 150)
|
||||
await img.paste(avatar, (125, 85))
|
||||
|
||||
frame = BuildImage(background=config.g_sResourcePath / "background/frame.png")
|
||||
# 头像框
|
||||
frame = BuildImage(
|
||||
background=config.g_sResourcePath / "background/frame/frame.png"
|
||||
)
|
||||
await img.paste(frame, (75, 44))
|
||||
|
||||
nameImg = await BuildImage.build_text_image(
|
||||
@@ -214,9 +251,37 @@ class CDarwFarm:
|
||||
)
|
||||
await img.paste(bondsImg, (570, 255))
|
||||
|
||||
@classmethod
|
||||
async def drawUserDecorateIcon(cls, img: BuildImage, player: CPlayer) -> bool:
|
||||
"""绘制用户装饰图标
|
||||
|
||||
Args:
|
||||
img (BuildImage): 主画布
|
||||
player (CPlayer): 用户
|
||||
|
||||
Returns:
|
||||
bool: 是否成功绘制
|
||||
"""
|
||||
|
||||
decorateIconId = player.user.get("decorateIcon", 0)
|
||||
if decorateIconId <= 0:
|
||||
return False
|
||||
|
||||
iconPath = (
|
||||
config.g_sResourcePath / f"decorateIcon/DecorateIcon{decorateIconId}.png"
|
||||
)
|
||||
if not iconPath.exists():
|
||||
return False
|
||||
|
||||
icon = BuildImage(background=iconPath)
|
||||
await icon.resize(0, 100, 100)
|
||||
await img.paste(icon, (x, y))
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def applyDefinitionResize(cls, img: BuildImage):
|
||||
"""根据配置调整绘制清晰度(original/medium/hight/其他)。"""
|
||||
"""根据配置调整绘制清晰度(original/medium/hight/其他)"""
|
||||
definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度")
|
||||
if definition == "medium":
|
||||
await img.resize(0.6)
|
||||
|
||||
@@ -5,11 +5,13 @@ from zoneinfo import ZoneInfo
|
||||
|
||||
from jinja2 import Template
|
||||
from playwright.async_api import async_playwright
|
||||
from zhdate import ZhDate
|
||||
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.message import MessageUtils
|
||||
|
||||
from ..core import CPlayer, g_pUserPool
|
||||
from . import utils
|
||||
|
||||
|
||||
class CToolManager:
|
||||
@@ -110,6 +112,47 @@ class CToolManager:
|
||||
tz = ZoneInfo("Asia/Shanghai")
|
||||
return datetime.now(tz)
|
||||
|
||||
@classmethod
|
||||
def getSeason(cls) -> utils.Season:
|
||||
month = cls.dateTime().month
|
||||
|
||||
if month in [3, 4, 5]:
|
||||
return utils.Season.SPRING
|
||||
elif month in [6, 7, 8]:
|
||||
return utils.Season.SUMMER
|
||||
elif month in [9, 10, 11]:
|
||||
return utils.Season.AUTUMN
|
||||
elif month in [12, 1, 2]:
|
||||
return utils.Season.WINTER
|
||||
else:
|
||||
return utils.Season.NO
|
||||
|
||||
@classmethod
|
||||
def getFestival(cls) -> utils.Festival:
|
||||
now = cls.dateTime()
|
||||
|
||||
lunarDate = ZhDate.from_datetime(now)
|
||||
lunarMonth = lunarDate.lunar_month
|
||||
lunarDay = lunarDate.lunar_day
|
||||
|
||||
festivalChecks = [
|
||||
(1, lambda: lunarMonth == 8 and lunarDay == 15), # 中秋节
|
||||
(2, lambda: (now.month, now.day) == (10, 1)), # 国庆节
|
||||
(3, lambda: lunarMonth == 1 and lunarDay == 15), # 元宵节
|
||||
(4, lambda: lunarMonth == 5 and lunarDay == 5), # 端午节
|
||||
(5, lambda: lunarMonth == 1 and lunarDay == 1), # 春节
|
||||
(6, lambda: (now.month, now.day) == (10, 31)), # 万圣节
|
||||
(7, lambda: (now.month, now.day) == (12, 25)), # 圣诞节
|
||||
(8, lambda: (now.month, now.day) == (1, 1)), # 新年
|
||||
(9, lambda: (now.month, now.day) == (2, 14)), # 情人节
|
||||
]
|
||||
|
||||
for code, checkFunc in festivalChecks:
|
||||
if checkFunc():
|
||||
return utils.Festival(code)
|
||||
|
||||
return utils.Festival.NO
|
||||
|
||||
@classmethod
|
||||
def renderHtmlToFile(
|
||||
cls, path: Path | str, context: dict, output: Path | str
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Festival(Enum):
|
||||
NO = 0 # 无节日
|
||||
MIDAUTUMN = 1 # 中秋节
|
||||
NATIONALDAY = 2 # 国庆节
|
||||
LANTERNFESTIVAL = 3 # 元宵节
|
||||
DRAGONBOAT = 4 # 端午节
|
||||
SPRINGFESTIVAL = 5 # 春节
|
||||
HALLOWEEN = 6 # 万圣节
|
||||
CHRISTMAS = 7 # 圣诞节
|
||||
NEWYEAR = 8 # 新年
|
||||
VALENTINE = 9 # 情人节
|
||||
|
||||
|
||||
class Season(Enum):
|
||||
NO = 0 # 无季节
|
||||
SPRING = 1 # 春季
|
||||
SUMMER = 2 # 夏季
|
||||
AUTUMN = 3 # 秋季
|
||||
WINTER = 4 # 冬季
|
||||
Reference in New Issue
Block a user