mirror of
https://github.com/Shu-Ying/main_plugin_farm.git
synced 2026-11-01 07:19:59 +08:00
🎨 使用Python包管理和模块访问
🎨 重构数据库结构, 使用集中式服务定位器与数据库交互 🎨 更改部分代码逻辑, 减小内存消耗
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
def getConfig():
|
||||
from . import config
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def getDrawFarm():
|
||||
from .darw.drawFarm import CDarwFarm
|
||||
|
||||
return CDarwFarm
|
||||
|
||||
|
||||
def getRequestManager():
|
||||
from .request import CRequestManager
|
||||
|
||||
return CRequestManager
|
||||
|
||||
|
||||
def getToolManager():
|
||||
from .tool import CToolManager
|
||||
|
||||
return CToolManager
|
||||
|
||||
|
||||
def getJsonManager():
|
||||
from .json import g_pJsonManager
|
||||
|
||||
return g_pJsonManager
|
||||
|
||||
|
||||
__all__ = [
|
||||
"getConfig",
|
||||
"getDrawFarm",
|
||||
"getJsonManager",
|
||||
"getRequestManager",
|
||||
"getToolManager",
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
from .drawFarm import CDarwFarm
|
||||
|
||||
__all__ = ["CDarwFarm"]
|
||||
@@ -0,0 +1,426 @@
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils._build_image import BuildImage
|
||||
from zhenxun.utils.image_utils import ImageTemplate
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ...core.dbService import g_pDBService
|
||||
from .. import config, getJsonManager, getToolManager
|
||||
|
||||
|
||||
class CDarwFarm:
|
||||
@classmethod
|
||||
async def drawFarmByUid(cls, uid: str) -> bytes:
|
||||
"""绘制用户农场
|
||||
|
||||
Args:
|
||||
uid (str): 用户UID
|
||||
|
||||
Returns:
|
||||
bytes: 返回绘制结果
|
||||
"""
|
||||
img, soilSize, soilPos, grass = await cls.createBaseCanvas()
|
||||
|
||||
player = await getToolManager().getPlayerByUid(uid)
|
||||
if not player:
|
||||
return img.pic2bytes()
|
||||
|
||||
soilUnlock = int(player.user.get("soil", 3))
|
||||
|
||||
isFirstExpansion = True # 首次添加扩建图片
|
||||
isFirstRipe = True
|
||||
|
||||
for index in range(0, 30):
|
||||
x = soilPos[str(index + 1)]["x"]
|
||||
y = soilPos[str(index + 1)]["y"]
|
||||
|
||||
# 已解锁格子
|
||||
if index < soilUnlock:
|
||||
isRipe = await cls.drawUnlockedTile(img, uid, index + 1, x, y, soilSize)
|
||||
if isRipe and isFirstRipe:
|
||||
await cls.pasteRipeOnce(img, x, y, soilSize)
|
||||
isFirstRipe = False
|
||||
else:
|
||||
addedExpansion = await cls.drawLockedTile(
|
||||
img, x, y, soilSize, grass, isFirstExpansion
|
||||
)
|
||||
if addedExpansion:
|
||||
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]:
|
||||
"""创建基础画布并返回 (img, soilSize, soilPos, grass)
|
||||
|
||||
Returns:
|
||||
tuple: (主画布, 土地尺寸, 土地坐标映射, 草地图)
|
||||
"""
|
||||
img = BuildImage(
|
||||
background=config.g_sResourcePath / "background/background.jpg"
|
||||
)
|
||||
|
||||
soil = await getJsonManager().getSoil()
|
||||
soilSize = soil["size"]
|
||||
soilPos = soil["soil"]
|
||||
|
||||
grass = BuildImage(background=config.g_sResourcePath / "soil/草土地.png")
|
||||
await grass.resize(0, soilSize[0], soilSize[1])
|
||||
|
||||
return img, soilSize, soilPos, grass
|
||||
|
||||
@classmethod
|
||||
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))。
|
||||
|
||||
Returns:
|
||||
bool: 是否有可收获作物(isRipe)。
|
||||
"""
|
||||
soilUrl = "soil/普通土地.png"
|
||||
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||
|
||||
if soilInfo:
|
||||
soilLevel = soilInfo.get("soilLevel", 0)
|
||||
if soilLevel == 1:
|
||||
soilUrl = "soil/红土地.png"
|
||||
elif soilLevel == 2:
|
||||
soilUrl = "soil/黑土地.png"
|
||||
elif soilLevel == 3:
|
||||
soilUrl = "soil/金土地.png"
|
||||
|
||||
soil = BuildImage(background=config.g_sResourcePath / soilUrl)
|
||||
await soil.resize(0, soilSize[0], soilSize[1])
|
||||
await img.paste(soil, (x, y))
|
||||
|
||||
isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant(
|
||||
uid, soilIndex
|
||||
)
|
||||
if isPlant and plant is not None:
|
||||
await img.paste(
|
||||
plant,
|
||||
(
|
||||
x + soilSize[0] // 2 - plant.width // 2 + offsetX,
|
||||
y + soilSize[1] // 2 - plant.height // 2 + offsetY,
|
||||
),
|
||||
)
|
||||
|
||||
return bool(isRipe)
|
||||
|
||||
@classmethod
|
||||
async def drawLockedTile(
|
||||
cls,
|
||||
img: BuildImage,
|
||||
x: int,
|
||||
y: int,
|
||||
soilSize,
|
||||
grass: BuildImage,
|
||||
isFirstExpansion: bool,
|
||||
) -> bool:
|
||||
"""绘制未解锁格子并在首次出现时绘制扩建图标。
|
||||
|
||||
Returns:
|
||||
bool: 是否在此格绘制了扩建图标(仅首次返回 True)。
|
||||
"""
|
||||
await img.paste(grass, (x, y))
|
||||
|
||||
if isFirstExpansion:
|
||||
expansion = BuildImage(
|
||||
background=config.g_sResourcePath / "background/expansion.png"
|
||||
)
|
||||
await expansion.resize(0, 69, 69)
|
||||
await img.paste(
|
||||
expansion,
|
||||
(
|
||||
x + soilSize[0] // 2 - expansion.width // 2,
|
||||
y + soilSize[1] // 2 - expansion.height,
|
||||
),
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def pasteRipeOnce(cls, img: BuildImage, x: int, y: int, soilSize):
|
||||
"""首次发现成熟作物时贴上成熟提示图(ripe)。"""
|
||||
ripe = BuildImage(background=config.g_sResourcePath / "background/ripe.png")
|
||||
await img.paste(
|
||||
ripe,
|
||||
(x + soilSize[0] // 2 - ripe.width // 2, y - ripe.height // 2),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def drawUserInfo(cls, img: BuildImage, player, uid: str):
|
||||
"""绘制左上角用户信息(头像、框、昵称、经验、等级、货币等)。
|
||||
TODO 需要详细的绘制头像框
|
||||
|
||||
Args:
|
||||
img (BuildImage): 主画布。
|
||||
player: 用户对象(包含 user 字段)。
|
||||
uid (str): 用户UID。
|
||||
"""
|
||||
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")
|
||||
await img.paste(frame, (75, 44))
|
||||
|
||||
nameImg = await BuildImage.build_text_image(
|
||||
player.user["name"], size=24, font_color=(77, 35, 4)
|
||||
)
|
||||
await img.paste(nameImg, (300, 92))
|
||||
|
||||
level = await player.getUserLevel()
|
||||
beginX = 309
|
||||
endX = 627
|
||||
width = int((level[2] / level[1]) * (endX - beginX)) if level[1] else 0
|
||||
await img.rectangle((beginX, 188, beginX + width, 222), (171, 194, 41))
|
||||
|
||||
# 经验
|
||||
expImg = await BuildImage.build_text_image(
|
||||
f"{level[2]} / {level[1]}", size=24, font_color=(102, 120, 19)
|
||||
)
|
||||
await img.paste(expImg, (390, 193))
|
||||
|
||||
# 等级
|
||||
levelImg = await BuildImage.build_text_image(
|
||||
str(level[0]), size=32, font_color=(214, 111, 1)
|
||||
)
|
||||
await img.paste(levelImg, (660, 187))
|
||||
|
||||
# 农场币
|
||||
pointImg = await BuildImage.build_text_image(
|
||||
str(player.user.get("point", 0)), size=24, font_color=(253, 253, 253)
|
||||
)
|
||||
await img.paste(pointImg, (330, 255))
|
||||
|
||||
# 点券
|
||||
bondsImg = await BuildImage.build_text_image(
|
||||
"0", size=24, font_color=(253, 253, 253)
|
||||
)
|
||||
await img.paste(bondsImg, (570, 255))
|
||||
|
||||
@classmethod
|
||||
async def applyDefinitionResize(cls, img: BuildImage):
|
||||
"""根据配置调整绘制清晰度(original/medium/hight/其他)。"""
|
||||
definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度")
|
||||
if definition == "medium":
|
||||
await img.resize(0.6)
|
||||
elif definition == "hight":
|
||||
await img.resize(0.8)
|
||||
elif definition == "original":
|
||||
pass
|
||||
else:
|
||||
await img.resize(0.4)
|
||||
|
||||
@classmethod
|
||||
async def drawDetailFarmByUid(cls, uid: str) -> list:
|
||||
"""绘制用户详细信息
|
||||
|
||||
Args:
|
||||
uid (str): 用户Uid
|
||||
|
||||
Returns:
|
||||
list:
|
||||
"""
|
||||
|
||||
info = []
|
||||
farm = await cls.drawFarmByUid(uid)
|
||||
player = await getToolManager().getPlayerByUid(uid)
|
||||
if not player:
|
||||
return info
|
||||
|
||||
info.append(BuildImage.open(farm))
|
||||
|
||||
dataList = []
|
||||
columnName = [
|
||||
"-",
|
||||
"土地ID",
|
||||
"土地等级",
|
||||
"作物名称",
|
||||
"成熟时间",
|
||||
"土地状态",
|
||||
"被偷数量",
|
||||
"剩余产出",
|
||||
]
|
||||
|
||||
icon = ""
|
||||
soilNumber = player.user.get("soil", 3)
|
||||
|
||||
for i in range(1, soilNumber + 1):
|
||||
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
|
||||
|
||||
if not soilInfo:
|
||||
continue
|
||||
|
||||
match soilInfo.get("soilLevel", 0):
|
||||
case 1:
|
||||
name = "红土地.png"
|
||||
case 2:
|
||||
name = "黑土地.png"
|
||||
case 3:
|
||||
name = "金土地.png"
|
||||
case _:
|
||||
name = "普通土地.png"
|
||||
iconPath = config.g_sResourcePath / "soil" / name
|
||||
|
||||
if iconPath.exists():
|
||||
icon = (iconPath, 33, 33)
|
||||
|
||||
plantName = soilInfo.get("plantName", "-")
|
||||
|
||||
if plantName == "-":
|
||||
matureTime = "-"
|
||||
soilStatus = "-"
|
||||
totalNumber = "-"
|
||||
plantNumber = "-"
|
||||
else:
|
||||
matureTime = (
|
||||
getToolManager()
|
||||
.dateTime()
|
||||
.fromtimestamp(int(soilInfo.get("matureTime", 0)))
|
||||
.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
soilStatus = await g_pDBService.userSoil.getUserSoilStatus(uid, i)
|
||||
|
||||
totalNumber = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
|
||||
planInfo = await g_pDBService.plant.getPlantByName(plantName)
|
||||
|
||||
if not planInfo:
|
||||
plantNumber = "None"
|
||||
else:
|
||||
plantNumber = f"{planInfo['harvest'] - totalNumber}"
|
||||
|
||||
dataList.append(
|
||||
[
|
||||
icon,
|
||||
i,
|
||||
await g_pDBService.userSoil.getSoilLevelText(soilInfo["soilLevel"]),
|
||||
plantName,
|
||||
matureTime,
|
||||
soilStatus,
|
||||
totalNumber,
|
||||
plantNumber,
|
||||
]
|
||||
)
|
||||
|
||||
if len(dataList) >= 15:
|
||||
result = await ImageTemplate.table_page(
|
||||
"土地详细信息",
|
||||
"",
|
||||
columnName,
|
||||
dataList,
|
||||
)
|
||||
|
||||
info.append(result.copy())
|
||||
dataList.clear()
|
||||
|
||||
if i >= soilNumber:
|
||||
result = await ImageTemplate.table_page(
|
||||
"土地详细信息",
|
||||
"",
|
||||
columnName,
|
||||
dataList,
|
||||
)
|
||||
|
||||
info.append(result.copy())
|
||||
dataList.clear()
|
||||
|
||||
return info
|
||||
|
||||
@classmethod
|
||||
async def drawSoilPlant(
|
||||
cls, uid: str, soilIndex: int
|
||||
) -> tuple[bool, BuildImage, bool, int, int]:
|
||||
"""绘制植物资源
|
||||
|
||||
Args:
|
||||
uid (str): 用户Uid
|
||||
soilIndex (int): 土地索引 从1开始
|
||||
|
||||
Returns:
|
||||
tuple[bool, BuildImage]: [绘制是否成功,资源图片, 是否成熟]
|
||||
"""
|
||||
|
||||
plant = None
|
||||
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||
|
||||
if not soilInfo:
|
||||
return False, None, False, 0, 0 # type: ignore
|
||||
|
||||
# 是否枯萎
|
||||
if int(soilInfo.get("wiltStatus", 0)) == 1:
|
||||
plant = BuildImage(background=config.g_sResourcePath / "plant/basic/9.png")
|
||||
await plant.resize(0, 150, 212)
|
||||
return True, plant, False, 0, 0
|
||||
|
||||
# 获取作物详细信息
|
||||
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"])
|
||||
if not plantInfo:
|
||||
logger.error(f"绘制植物资源失败: {soilInfo['plantName']}")
|
||||
return False, None, False, 0, 0 # type: ignore
|
||||
|
||||
offsetX = plantInfo.get("officX", 0)
|
||||
offsetY = plantInfo.get("officY", 0)
|
||||
offsetW = plantInfo.get("officW", 0)
|
||||
offsetH = plantInfo.get("officH", 0)
|
||||
|
||||
currentTime = getToolManager().dateTime().now().timestamp()
|
||||
phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo["plantName"])
|
||||
|
||||
# 如果当前时间大于成熟时间 说明作物成熟
|
||||
if currentTime >= soilInfo["matureTime"]:
|
||||
plant = BuildImage(
|
||||
background=config.g_sResourcePath
|
||||
/ f"plant/{soilInfo['plantName']}/{len(phaseList)}.png"
|
||||
)
|
||||
|
||||
return True, plant, True, offsetX, offsetY
|
||||
else:
|
||||
# 如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片
|
||||
# if soilInfo["harvestCount"] >= 1:
|
||||
# plant = BuildImage(
|
||||
# background=g_sResourcePath
|
||||
# / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1}.png"
|
||||
# )
|
||||
|
||||
# return True, plant, False, offsetX, offsetY
|
||||
|
||||
# 如果没有成熟 则根据当前阶段进行绘制
|
||||
elapsedTime = currentTime - soilInfo["plantTime"]
|
||||
currentStage = currentStage = sum(
|
||||
1 for thr in phaseList if elapsedTime >= thr
|
||||
)
|
||||
|
||||
if currentStage <= 0:
|
||||
if not plantInfo["general"]:
|
||||
plant = BuildImage(
|
||||
background=config.g_sResourcePath
|
||||
/ f"plant/{soilInfo['plantName']}/0.png"
|
||||
)
|
||||
else:
|
||||
plant = BuildImage(
|
||||
background=config.g_sResourcePath / "plant/basic/0.png"
|
||||
)
|
||||
await plant.resize(0, 35 + offsetW, 58 + offsetH)
|
||||
else:
|
||||
plant = BuildImage(
|
||||
background=config.g_sResourcePath
|
||||
/ f"plant/{soilInfo['plantName']}/{currentStage}.png"
|
||||
)
|
||||
|
||||
return True, plant, False, offsetX, offsetY
|
||||
+2
-3
@@ -2,8 +2,7 @@ import json
|
||||
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from . import config
|
||||
from .request import g_pRequestManager
|
||||
from . import config, getRequestManager
|
||||
|
||||
|
||||
class CJsonManager:
|
||||
@@ -117,7 +116,7 @@ class CJsonManager:
|
||||
return False
|
||||
|
||||
async def initSignInFile(self) -> bool:
|
||||
if not await g_pRequestManager.initSignInFile():
|
||||
if not await getRequestManager().initSignInFile():
|
||||
config.g_bSignStatus = False
|
||||
return False
|
||||
else:
|
||||
|
||||
+10
-14
@@ -15,8 +15,7 @@ from zhenxun.configs.config import Config
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from ..core.dbService import g_pDBService
|
||||
from .config import g_sPlantPath, g_sSignInPath
|
||||
from .tool import g_pToolManager
|
||||
from . import config, getToolManager
|
||||
|
||||
|
||||
class CRequestManager:
|
||||
@@ -162,14 +161,14 @@ class CRequestManager:
|
||||
|
||||
@classmethod
|
||||
async def initSignInFile(cls) -> bool:
|
||||
if os.path.exists(g_sSignInPath):
|
||||
if os.path.exists(config.g_sSignInPath):
|
||||
try:
|
||||
with open(g_sSignInPath, encoding="utf-8") as f:
|
||||
with open(config.g_sSignInPath, encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
sign = json.loads(content)
|
||||
|
||||
date = sign.get("date", "")
|
||||
yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m")
|
||||
yearMonth = getToolManager().dateTime().now().strftime("%Y%m")
|
||||
|
||||
if date == yearMonth:
|
||||
logger.debug("真寻农场签到文件检查完毕")
|
||||
@@ -193,8 +192,8 @@ class CRequestManager:
|
||||
try:
|
||||
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||
url = f"{baseUrl.rstrip('/')}:8998/sign_in"
|
||||
path = str(g_sSignInPath.parent.resolve(strict=False))
|
||||
yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m")
|
||||
path = str(config.g_sSignInPath.parent.resolve(strict=False))
|
||||
yearMonth = getToolManager().dateTime().now().strftime("%Y%m")
|
||||
|
||||
# 下载为 signTemp.json
|
||||
success = await cls.download(
|
||||
@@ -208,7 +207,7 @@ class CRequestManager:
|
||||
return False
|
||||
|
||||
# 重命名为 sign_in.json
|
||||
g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json")
|
||||
getToolManager().renameFile(f"{path}/signTemp.json", "sign_in.json")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error("下载签到文件失败", e=e)
|
||||
@@ -221,7 +220,7 @@ class CRequestManager:
|
||||
Returns:
|
||||
bool: 是否为最新版或成功更新
|
||||
"""
|
||||
versionPath = os.path.join(os.path.dirname(g_sPlantPath), "version.json")
|
||||
versionPath = os.path.join(os.path.dirname(config.g_sPlantPath), "version.json")
|
||||
|
||||
try:
|
||||
with open(versionPath, encoding="utf-8") as f:
|
||||
@@ -262,7 +261,7 @@ class CRequestManager:
|
||||
"""
|
||||
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||
|
||||
savePath = os.path.dirname(g_sPlantPath)
|
||||
savePath = os.path.dirname(config.g_sPlantPath)
|
||||
success = await cls.download(
|
||||
url=f"{baseUrl.rstrip('/')}:8998/file/plant.db",
|
||||
savePath=savePath,
|
||||
@@ -273,7 +272,7 @@ class CRequestManager:
|
||||
return False
|
||||
|
||||
# 重命名为 sign_in.json
|
||||
g_pToolManager.renameFile(f"{savePath}/plantTemp.db", "plant.db")
|
||||
getToolManager().renameFile(f"{savePath}/plantTemp.db", "plant.db")
|
||||
|
||||
versionPath = os.path.join(savePath, "version.json")
|
||||
try:
|
||||
@@ -288,6 +287,3 @@ class CRequestManager:
|
||||
await g_pDBService.plant.downloadPlant()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
g_pRequestManager = CRequestManager()
|
||||
|
||||
+1
-5
@@ -9,8 +9,7 @@ from playwright.async_api import async_playwright
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.message import MessageUtils
|
||||
|
||||
from ..core.player.player import CPlayer
|
||||
from ..core.player.playerPool import g_pUserPool
|
||||
from ..core import CPlayer, g_pUserPool
|
||||
|
||||
|
||||
class CToolManager:
|
||||
@@ -192,6 +191,3 @@ class CToolManager:
|
||||
# 截图并保存到本地文件
|
||||
await page.screenshot(path=save, full_page=True)
|
||||
await browser.close()
|
||||
|
||||
|
||||
g_pToolManager = CToolManager()
|
||||
|
||||
Reference in New Issue
Block a user