SFGame_Tool/main.py

503 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding:utf-8 -*-
import configparser
import os
import pathlib
import re
import subprocess
import sys
import time
from configparser import ExtendedInterpolation
# import winreg
from subprocess import Popen
import psutil
import pymysql
import requests
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
# 界面文件为 mainui.py
from mainui import *
configFile = 'config.ini'
# 创建配置文件对象
config = configparser.ConfigParser(interpolation=ExtendedInterpolation(), inline_comment_prefixes=['#', ';'], allow_no_value=True)
# 读取配置文件
config.read(configFile, encoding='utf-8')
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.apktool_path = config['TOOL_PATH']['apktool_path']
self.android_name = config['ANDROID']['android_name']
self.android_name_file = config['ANDROID']['android_name_file']
self.tmp_android_name = config['ANDROID']['tmp_android_name']
self.new_android_name = config['ANDROID']['new_android_name']
# 初始赋值
self.nowIpLabel.setText(config['GAME']['now_ip'])
self.newIpInput.setText(config['GAME']['new_ip'])
self.nowGameLabel.setText(config['GAME']['game_name'])
self.newGameInput.setText(config['GAME']['game_name'])
# 设置按钮状态
if not config.has_section('NGINX') and not config.has_section('APACHE'):
self.nginxButton.setDisabled(True)
elif config.has_section('APACHE'):
self.nginxButton.setText("启动Apache")
if isinstance(checkprocess("httpd.exe"), int):
self.nginxButton.setText("关闭Apache")
else:
self.nginxButton.setText("启动Nginx")
if isinstance(checkprocess("nginx.exe"), int):
self.nginxButton.setText("关闭Nginx")
if not config.has_section('MYSQL'):
self.mysqlButton.setDisabled(True)
else:
if isinstance(checkprocess("mysqld.exe"), int):
self.mysqlButton.setText("关闭MySql")
else:
self.mysqlButton.setText("启动MySql")
if not config.has_section('WEB'):
self.gmurlButton.setDisabled(True)
self.yyurlButton.setDisabled(True)
else:
self.gmurlButton.setToolTip("<html><head/><body><p>网址:" + config['WEB']['gm_url'] + "<br/>帐号:" + config['WEB']['gm_user'] + "<br/>密码:" + config['WEB']['gm_pass'] + "<br/>校验码:" + config['WEB']['gm_code'] + "</p></body></html>")
self.yyurlButton.setToolTip("<html><head/><body><p>网址:" + config['WEB']['game_url'] + "<br/>帐号:" + config['WEB']['game_user'] + "<br/>密码:" + config['WEB']['game_pass'] + "<br/>校验码:" + config['WEB']['game_code'] + "</p></body></html>")
if config.has_section('ANDROID'):
self.fanbianyiButton.setToolTip("反编译的APK路径<br>" + config['ANDROID']['android_name'] + "<br>反编译后的文件路径:<br>" + config['ANDROID']['android_name_file'])
self.shengchengButton.setToolTip("生成的APK路径<br>" + config['ANDROID']['tmp_android_name'])
self.qianmingButton.setToolTip("签名后的APK路径<br>" + config['ANDROID']['new_android_name'] + "<br>密钥路径:<br>" + config['TOOL_PATH']['apktool_path'] + "key.keystore")
# 初始化JAVA配置
if not os.getenv('JAVA_HOME'):
java_path = config['TOOL_PATH']['java_path']
os.environ['JAVA_HOME'] = java_path
os.environ['path'] = os.getenv('path') + java_path + 'bin;' + java_path + 'jre\\bin;'
os.environ['CLASSPATH'] = java_path + 'bin;' + java_path + 'lib\\dt.jar;' + java_path + 'lib\\tools.jar;'
# print(os.getenv('JAVA_HOME'))
# print(os.getenv('CLASSPATH'))
# print(os.getenv('path'))
def saveSet(self, event):
print('保存设置,按钮被单击')
message = QMessageBox.information(self, "提示信息", "确定要保存设置吗?", QMessageBox.Ok | QMessageBox.No, QMessageBox.No)
if message == QMessageBox.No:
return
# 取值
now_ip = config['GAME']['now_ip']
new_ip = self.newIpInput.text()
now_game = config['GAME']['game_name']
new_game = self.newGameInput.text()
compile_ip = re.compile('^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
# 验证IP地址格式
if (new_ip == "") or new_game == "" or not (compile_ip.match(new_ip)):
QMessageBox.information(self, "提示信息", "IP地址、游戏名称为空或有误请正确填写IPv4地址或游戏名称!")
return
# 处理SQL语句
if config.has_section('GAME_DATA_SQL'):
sqls = []
for key, value in config.items('GAME_DATA_SQL'):
print(key, value)
sql = value.replace('~【game_ip】~', new_ip)
sql = sql.replace('~【game_name】~', new_game)
sqls.append(sql)
print(key, sql)
# 判断MYSQL是否启动
if sqls:
if isinstance(checkprocess("mysqld.exe"), int):
mysql_run(sqls)
else:
message = QMessageBox.information(self, "提示信息", "MYSQL尚未启动数据库相关脚本无法执行是否现在启动MYSQL", QMessageBox.Ok | QMessageBox.No, QMessageBox.Ok)
if message == QMessageBox.Ok:
self.mysql(self.mysqlButton) # 触发MYSQL启动按钮
sum = 0
# 以0.5秒的频次检测MYSQL是否启动超过20次则超时
while not isinstance(checkprocess("mysqld.exe"), int):
if sum > 20:
QMessageBox.information(self, "提示信息", "MYSQL启动失败请重新尝试")
return
time.sleep(0.5)
sum = sum + 1
mysql_run(sqls)
else:
return
# 配置文件处理
sum = 0
for key, value in config.items('GAME_FILE_PATH'):
print(key, value)
# 处理文件编码
if len(value.split(",")) < 2:
code = config['GAME']['file_encoding']
else:
code = value.split(",")[1]
filename = value.split(",")[0]
# 执行文件内容替换
try:
with open(filename, 'rt+', encoding=code) as f:
t = f.read()
t = t.replace(now_ip, new_ip)
t = t.replace(now_game, new_game)
# 读写偏移位置移到最开始处
f.seek(0, 0)
f.write(t)
# 设置文件结尾 EOF
f.truncate()
sum = sum + 1
except:
QMessageBox.information(self, "提示信息", "替换“" + filename + "”出错,可能文件不存在或编码不对,请检查需要替换的文件编码与配置文件编码是否相符!")
return
# 写入配置文件
config.set('GAME', 'game_name', new_game)
config.set('GAME', 'now_ip', new_ip)
config.set('GAME', 'new_ip', new_ip)
saveconfig = open(configFile, 'wt', encoding=config['GAME']['file_encoding'])
config.write(saveconfig) # 把要修改的节点的内容写到文件中
saveconfig.close()
# 设置按钮文字变化
self.nowGameLabel.setText(new_game)
self.nowIpLabel.setText(new_ip)
# 成功提示
QMessageBox.information(self, "提示信息", "配置保存成功!重启游戏后生效!<br>游戏名称:" + new_game + "----游戏IP" + new_ip + "<br>共配置了" + str(sum) + "个文件")
print('修改完成:' + new_game + ':' + new_ip)
def nginx(self, event):
if self.nginxButton.text() == '启动Nginx':
Popen('start .\\nginx.exe',
shell=True,
cwd=config['NGINX']['nginx_path'],
# encoding='utf-8'
)
Popen(
config['PHP']['php_spawner_path'] + 'php-cgi-spawner.exe "' + config['PHP']['php_path'] + 'php-cgi.exe -c ' + config['PHP']['php_path'] + 'php.ini" ' + config['PHP']['php_cgi_port'] + ' 1+16',
shell=True,
# cwd=config['GAME']['dir'] + 'phpstudy_pro',
# encoding='utf-8'
)
self.nginxButton.setText("关闭Nginx")
elif self.nginxButton.text() == '关闭Nginx':
Popen('.\\nginx.exe -s stop',
shell=True,
cwd=config['NGINX']['nginx_path'],
# encoding='utf-8'
)
Popen('taskkill /f /t /im php-cgi-spawner.exe',
shell=True,
# encoding='utf-8'
)
Popen('taskkill /f /t /im php-cgi.exe',
shell=True,
# encoding='utf-8'
)
self.nginxButton.setText("启动Nginx")
elif self.nginxButton.text() == '启动Apache':
Popen('.\\httpd.exe',
shell=True,
cwd=config['APACHE']['apache_path'] + 'bin\\',
# encoding='utf-8'
)
self.nginxButton.setText("关闭Apache")
elif self.nginxButton.text() == '关闭Apache':
Popen('.\\httpd.exe k stop',
shell=True,
cwd=config['APACHE']['apache_path'] + 'bin\\',
# encoding='utf-8'
)
Popen('taskkill /f /t /im httpd.exe',
shell=True,
# encoding='utf-8'
)
self.nginxButton.setText("启动Apache")
# print(process)
# os.chdir("D:\\LYserver\\phpstudy_pro\\Extensions\\Nginx1.15.11")
# nginxtype = os.system('start .\\nginx.exe') # 不弹出界面
# if nginxtype
# os.system('start .\\nginx.exe') #弹出界面
print('启动Nginx,按钮被单击')
def mysql(self, event):
if self.mysqlButton.text() == '启动MySql':
Popen('.\\mysqld.exe --console',
shell=True,
cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
self.mysqlButton.setText("关闭MySql")
else:
Popen('taskkill /f /t /im mysqld.exe',
shell=True,
# encoding='utf-8'
)
self.mysqlButton.setText("启动MySql")
print('启动MySql,按钮被单击')
def game(self, event):
if self.gameButton.text() == '启动游戏':
# 读取游戏启动路径
for key, value in config.items('GAME_PATH'):
print(key, value)
# 处理启动时间
if len(value.split(",")) < 2:
sleeptime = 0
else:
sleeptime = float(value.split(",")[1])
game_file = value.split(",")[0]
# 处理启动路径
game_path = '\\'.join(value.split("\\")[0:-1])
# print(game_path)
try:
Popen('start ' + game_file,
shell=True,
cwd=game_path,
# encoding='utf-8'
)
time.sleep(float(sleeptime)) # 延迟启动时间
except:
QMessageBox.information(self, "提示信息", "启动“" + game_file + "”出错,可能文件不存在或配置不对!")
return
self.gameButton.setText("关闭游戏")
else:
# 读取游戏启动路径
for key, value in config.items('GAME_PATH'):
print(key, value)
# 获取进程名称并结束进程
game_file = value.split(",")[0].split("\\")
game_file = game_file[len(game_file) - 1]
try:
Popen(
'taskkill /f /t /im ' + game_file,
shell=True,
# encoding='utf-8'
)
except:
QMessageBox.information(self, "提示信息", "关闭游戏进程:“" + game_file + "”出错!")
return
# Popen(
# 'taskkill /f /t /im LocalLogServer64_R.exe & taskkill /f /t /im LoggerServer64_R.exe & taskkill /f /t /im SessionServer64_R.exe & taskkill /f /t /im NameServer64_R.exe & taskkill /f /t /im LogicServerCQ64_R.exe & taskkill /f /t /im GateServer64_R.exe & taskkill /f /t /im DBCenterServer64_R.exe & taskkill /f /t /im DBServer64_R.exe & taskkill /f /t /im BackStageServer64_R.exe & taskkill /f /t /im AMServer64_R.exe',
# shell=True,
# # encoding='utf-8'
# )
self.gameButton.setText("启动游戏")
print('启动游戏,按钮被单击')
def fanbianyi(self, event):
obj = self.fanbianyiButton
# obj.setText('运行中')
title = obj.text()
cmd = 'apktool d ' + self.android_name + ' -f -o ' + self.android_name_file
cwd = self.apktool_path
code = "gbk"
path = self.android_name_file
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('反编译APK,按钮被单击')
def shengcheng(self, event):
obj = self.shengchengButton
# obj.setText('运行中')
title = obj.text()
cmd = 'apktool b ' + self.android_name_file + ' -o ' + self.tmp_android_name
cwd = self.apktool_path
code = "gbk"
path = self.tmp_android_name
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('生成APK,按钮被单击')
def qianming(self, event):
obj = self.qianmingButton
# obj.setText('运行中')
title = obj.text()
cmd = 'jarsigner -verbose -keystore key.keystore -storepass 123456 -signedjar ' + self.new_android_name + ' ' + self.tmp_android_name + ' key.keystore'
cwd = self.apktool_path
code = "gbk"
path = self.new_android_name
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('APK签名,按钮被单击')
def heidisql(self, event):
cmd = 'start "" ' + config['TOOL_PATH']['heidisql_path']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('heidisql,按钮被单击')
def gmurl(self, event):
cmd = 'start ' + config['WEB']['gm_url']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('gmurl,按钮被单击')
# TODO 增加自动登录GM后台功能
def yyurl(self, event):
cmd = 'start ' + config['WEB']['game_url']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('yyurl,按钮被单击')
# TODO 增加自动登录后台功能
# VC运行库
def msvbcrt(self, event):
cmd = 'start "" ' + config['TOOL_PATH']['msvbcrt_path']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('VC运行库,按钮被单击')
# 程序升级
def up(self, event):
if self.upButton.text() == '检查新版' or self.upButton.text() == '无新版本':
newVersion = getNewVersion() # 获取新版本号
# if os.path.isfile("upgrade.bat"): # 判断是否有upgrade.bat这个文件有就删除
# os.remove("upgrade.bat")
if version < newVersion: # 判断当前程序是否是最新版本
self.upButton.setText("新版本:" + str(newVersion))
else:
self.upButton.setText("无新版本")
print('当前版本:', version, '\t最新版本:', newVersion)
else:
cmd = 'start ' + 'https://shileiye.com/'
Popen(cmd,
shell=True,
)
print('检查新版,按钮被单击')
# TODO 增加自动下载更新功能
# 利用新线程进行脚本执行回显
def add_cmd_msg(self, cmd, cwd="", title="", obj="", path="", code="utf8"):
obj.setEnabled(False)
self.CMDtextEdit.clear()
self.CMDtextEdit.append(title + "脚本开始执行!")
self.work = Thread(cmd, cwd, title, obj, path, code)
self.work.trigger.connect(self.show_msg) # 线程中的trigger与主类中的方法进行绑定
self.work.daemon = True # 主进程退出则该线程也退出
self.work.start() # 开启线程
# 更新UI方法
def show_msg(self, str):
self.CMDtextEdit.moveCursor(QTextCursor.End)
self.CMDtextEdit.append(str)
# self.CMDtextEdit.insertPlainText(str)
# self.CMDtextEdit.insertHtml(str)
# 线程类
class Thread(QThread):
trigger = pyqtSignal(str) # 注意pyqtSignal一定要实例到__init__前面
def __init__(self, cmd, cwd="", title="", obj="", path="", code="utf8"):
super(Thread, self).__init__()
# 定义的变量
self.cmd = cmd
self.cwd = cwd
self.title = title
self.obj = obj
self.path = path
self.code = code
self.state = True
# 执行耗时操作
def run(self):
process = subprocess.Popen(self.cmd, shell=True, cwd=self.cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while process.poll() is None:
line = process.stdout.readline()
line = line.strip()
if line:
self.trigger.emit(line.decode(self.code, 'ignore'))
print(line.decode(self.code, 'ignore'))
self.trigger.emit(self.title + '脚本执行结束!')
self.obj.setEnabled(True)
# self.obj.setText(self.title)
# print(self.obj)
# self.fanbianyiButton.setEnabled(False)
# self.fanbianyiButton.setText('运行中')
if self.path != "":
self.trigger.emit('文件路径:' + self.path)
startfile(self.path)
# MYSQL操作函数
def mysql_run(sqls, table=""):
print(sqls)
# 打开数据库连接
db = pymysql.connect(host=config['MYSQL']['mysql_host'],
user=config['MYSQL']['mysql_user'],
password=config['MYSQL']['mysql_pass'],
database=table,
port=int(config['MYSQL']['mysql_port'])
)
# 使用cursor()方法获取操作游标
cursor = db.cursor()
for sql in sqls:
try:
# 执行SQL语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
print('更新成功')
except:
print('出错了')
# 发生错误时回滚
db.rollback()
# 关闭数据库连接
db.close()
print('MySql执行完毕')
# 进程判断返回进程ID
def checkprocess(processname):
pl = psutil.pids()
for pid in pl:
if psutil.Process(pid).name() == processname:
return pid
# 打开文件或文件夹,并且选中文件
def startfile(filename):
try:
os.startfile(f'explorer /select, "{pathlib.Path(filename)}')
except:
subprocess.Popen(f'explorer /select, "{pathlib.Path(filename)}')
# 获取最新程序版本号
def getNewVersion():
url = r'http://127.0.0.1:82/Version.txt'
try:
value = requests.get(url, timeout=10).content.decode()
except:
value = 0
return float(value)
if __name__ == '__main__':
version = 1.0
app = QApplication(sys.argv)
myWin = MainWindow()
myWin.show()
sys.exit(app.exec_())