windows下搭建服务器
login anonymous
force_install_dir ./palworld
app_update 2394010 validate
通过python实现自动备份、重启和启动时检测更新
引入依赖包
pip install psutil
pip install schedule
代码实现
以下代码放置steamcmd.exe同级目录
import subprocess
import psutil
import shutil
import schedule
import time
import os
import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64
name = "幻兽帕鲁"
smtp_server = "smtp.126.com"
smtp_port = 25
smtp_email = "darktool@126.com"
smtp_password = "XXXXXXXXXX"
to_email = "1529576424@qq.com"
def send_email_smtp(receiver_email, subject, body):
# 创建邮件对象
message = MIMEMultipart()
message["From"] = f'"=?utf-8?B?{base64.b64encode((name + "-监控").encode("utf-8")).decode("utf-8")}?=" <{smtp_email}>'
message["To"] = receiver_email
message["Subject"] = subject
# 邮件正文
message.attach(MIMEText(body, "plain"))
print(f"发送邮件\nTo:{receiver_email}\n主题:{subject}\n正文:{body}")
try:
# 连接到 SMTP 服务器
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls() # 启用安全传输
server.login(smtp_email, smtp_password)
# 发送邮件
server.send_message(message)
print("发送邮件成功!")
server.quit()
except Exception as e:
print(f"发送邮件失败!\nError: {e}")
program_name = "PalServer-Win64-Shipping-Cmd.exe" # 程序名
program_directory = "C:\steamcmd\palworld" # 程序目录
program_start_command = "PalServer.exe" # 程序启动命令
steam_app_path = "./palworld" # 指定程序安装目录
steam_app_id = "2394010" # 程序id
xml_list = [
{
"source": "C:\steamcmd\palworld\DefaultPalWorldSettings - 副本.ini",
"target": "C:\steamcmd\palworld\DefaultPalWorldSettings.ini"
},
{
"source": "C:\steamcmd\palworld\PalWorldSettings - 副本.ini",
"target": "C:\steamcmd\palworld\Pal\Saved\Config\WindowsServer\PalWorldSettings.ini"
}
]
backup_list = [
# {
# "source": "C:\steamcmd\palworld\Pal\Saved",
# "target": "C:\steamcmd\palworld\Pal\\back"
# }
]
# # 调用函数进行备份
# backup_files('/原始目录', '/目标目录')
def backup_files(source_dir, dest_dir):
try:
shutil.copytree(source_dir, dest_dir)
print("文件备份成功!")
except Exception as e:
print("文件备份失败:", str(e))
# 备份任务
def backup_job():
for backup in backup_list:
source_directory = backup["source"]
target_directory = backup["target"]
current_time = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
directory = target_directory + "\\" + current_time
backup_files(source_directory, directory)
print(f"{source_directory}备份时间:", current_time)
# 检测程序是否在执行
def check_process_running(process_name):
for process in psutil.process_iter(['name', 'cmdline']):
if process.info['name'] and process.info['name'] == process_name:
return True
elif process.info['cmdline'] and process_name in process.info['cmdline']:
return True
return False
# 执行steamcmd更新命令
def run_steamcmd_update_command_realtime():
# 运行SteamCMD命令并实时获取输出
process = subprocess.Popen(['steamcmd.exe', '+login', 'anonymous', '+force_install_dir', steam_app_path, '+app_update', steam_app_id, 'validate', '+quit'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
encoding='utf-8')
# 实时获取控制台输出内容
for line in process.stdout:
print(line, end='')
# 等待命令执行完毕
process.wait()
# 检查是否有错误发生
if process.returncode != 0:
print(f"Command execution failed with exit code {process.returncode}")
# 拷贝配置文件,防止更新时配置文件被还原
for xml in xml_list:
try:
source = xml["source"]
target = xml["target"]
shutil.copy2(source, target)
print(f"拷贝{source}配置文件到{target}成功!")
except Exception as e:
print(f"拷贝{source}配置文件到{target}失败:", str(e))
# 关闭服务器
def stop_program():
print("服务器关闭时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
if sys.platform.startswith('win'):
subprocess.Popen('taskkill /F /IM ' + program_name, shell=True)
# elif sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
# subprocess.Popen(['pkill', '-f', 'your_program.py'])
# 启动服务器
def start_program():
print("服务器启动时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
if sys.platform.startswith('win'):
# subprocess.Popen(program_path, shell=True)
# subprocess.run([program_start_command], input=b'\n', shell=True, cwd=program_directory)
process = subprocess.Popen([program_start_command],
shell=True,
cwd=program_directory,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
encoding='gbk')
# # 实时获取控制台输出内容
# for line in process.stdout:
# out = process.stdout.readline()
# print(out, end='')
# if out == '' and process.poll() is not None:
# break
# elif '请按任意键继续. . .' in out:
# # 当输出中出现特定内容时
# process.stdin.write('\n')
# process.stdin.flush() # 确保输入被发送
# #
time.sleep(10) # 等待10秒
# 强制终止子进程
process.kill()
# 等待进程结束
process.wait()
# 检查是否有错误发生
if process.returncode != 0:
print(f"Command execution failed with exit code {process.returncode}")
# elif sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
# subprocess.Popen(['python', 'your_program.py'])
# 重启任务
restarting = False
def stop_and_restart_job():
# 在更改全局变量前先声明
global restarting # 声明restarting为全局变量
restarting = True
while check_process_running(program_name): # 等待程序结束完成
stop_program()
time.sleep(10) # 等待10秒,确保程序完全关闭
print("启动程序更新")
run_steamcmd_update_command_realtime()
while not check_process_running(program_name): # 查询程序是否启动,如果未启动则再次启动
start_program()
time.sleep(60)
restarting = False
send_email_smtp(to_email, "程序定时重启", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 当程序崩溃时自动启动
start_time = 0
def auto_start_job():
# 在更改全局变量前先声明
global start_time # 声明start_time为全局变量
if not restarting and not check_process_running(program_name):
print("检测到服务器未启动,开始自启动:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
run_steamcmd_update_command_realtime()
start_program()
d = time.time() - start_time
print(f"距离上一封自启动邮件发送时间间隔:{d}秒")
if d > 1800: # 确保每半个小时内该邮件只会发送一份
send_email_smtp(to_email, "检测到程序未执行,执行自动启动", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
start_time = time.time()
# 定义任务调度函数
def schedule_job():
schedule.every(1).minutes.do(auto_start_job) # 每分钟检测一次程序是否崩溃
schedule.every(30).minutes.do(backup_job) # 每隔半小时执行一次备份
schedule.every().day.at("11:53").do(stop_and_restart_job) # 每天11:53重启服务器
# 启动任务调度器
schedule_job()
print(f"检测{program_name}程序是否在执行:{check_process_running(program_name)}")
# if not check_process_running(program_name):
# print("程序未执行,启动程序更新")
# run_steamcmd_update_command_realtime()
# start_program()
while True:
schedule.run_pending() # 检查是否有任务需要执行
time.sleep(1) # 避免过于频繁的检查
配置文件翻译
Difficulty=None, ; 难度,None 或 Difficulty
DayTimeSpeedRate=1.000000, ; 白天流逝速度倍率
NightTimeSpeedRate=1.000000, ; 夜晚流逝速度倍率
ExpRate=1.000000, ; 经验值倍率
PalCaptureRate=1.000000, ; 捕捉概率倍率
PalSpawnNumRate=1.000000, ; 帕鲁出现数量倍率
PalDamageRateAttack=1.000000, ; 帕鲁攻击伤害倍率
PalDamageRateDefense=1.000000, ; 帕鲁承受伤害倍率
PlayerDamageRateAttack=1.000000, ; 玩家攻击伤害倍率
PlayerDamageRateDefense=1.000000, ; 玩家承受伤害倍率
PlayerStomachDecreaceRate=1.000000, ; 玩家饱食度降低倍率
PlayerStaminaDecreaceRate=1.000000, ; 玩家耐力降低倍率
PlayerAutoHPRegeneRate=1.000000, ; 玩家生命值自然恢复倍率
PlayerAutoHpRegeneRateInSleep=1.000000, ; 玩家睡眠时生命恢复倍率
PalStomachDecreaceRate=1.000000, ; 帕鲁饱食度降低倍率
PalStaminaDecreaceRate=1.000000, ; 帕鲁耐力降低倍率
PalAutoHPRegeneRate=1.000000, ; 帕鲁生命值自然恢复倍率
PalAutoHpRegeneRateInSleep=1.000000, ; 帕鲁睡眠时生命恢复倍率
BuildObjectDamageRate=1.000000, ; 对建筑物伤害倍率
BuildObjectDeteriorationDamageRate=1.000000, ; 建筑物劣化速度倍率
CollectionDropRate=1.000000, ; 可采集物品掉落倍率
CollectionObjectHpRate=1.000000, ; 可采集物品生命值倍率
CollectionObjectRespawnSpeedRate=1.000000, ; 可采集物品生成速率
EnemyDropItemRate=1.000000, ; 敌方掉落物品倍率
DeathPenalty=All, ; 死亡惩罚,None 不掉落,Item 只掉物品不掉装备,ItemAndEquipment 掉物品和装备,All 全都掉
bEnablePlayerToPlayerDamage=False, ; 启用玩家对玩家伤害功能
bEnableFriendlyFire=False, ; 启用友军伤害功能
bEnableInvaderEnemy=True, ; 启用袭击事件功能
bActiveUNKO=False, ; 启用 UNKO 功能
bEnableAimAssistPad=True, ; 启用手柄瞄准辅助功能
bEnableAimAssistKeyboard=False, ; 启用键盘瞄准辅助功能
DropItemMaxNum=3000, ; 掉落物品最大数量
DropItemMaxNum_UNKO=100, ; 掉落物品最大数量_UNKO
BaseCampMaxNum=128, ; 大本营最大数量
BaseCampWorkerMaxNum=15, ; 大本营工人最大数量
DropItemAliveMaxHours=1.000000, ; 掉落物品存在最大时长(小时)
bAutoResetGuildNoOnlinePlayers=False, ; 自动重置没有在线玩家的公会
AutoResetGuildTimeNoOnlinePlayers=72.000000, ; 无在线玩家时自动重置公会的时间(小时)
GuildPlayerMaxNum=20, ; 公会玩家最大数量
PalEggDefaultHatchingTime=72.000000, ; 帕鲁蛋默认孵化时间(小时)
WorkSpeedRate=1.000000, ; 工作速度倍率
bIsMultiplay=False, ; 是否为多人游戏
bIsPvP=False, ; 是否为 PvP 游戏
bCanPickupOtherGuildDeathPenaltyDrop=False, ; 是否可以拾取其他公会的死亡掉落物
bEnableNonLoginPenalty=True, ; 是否启用不登录惩罚
bEnableFastTravel=True, ; 是否启用快速旅行
bIsStartLocationSelectByMap=True, ; 是否通过地图选择起始位置
bExistPlayerAfterLogout=False, ; 是否在登出后保留玩家
bEnableDefenseOtherGuildPlayer=False, ; 是否启用对其他公会玩家的防御
CoopPlayerMaxNum=4, ; 合作玩家最大数量
ServerPlayerMaxNum=32, ; 服务器玩家最大数量
ServerName="Default Palworld Server", ; 服务器名称
ServerDescription="", ; 服务器描述
AdminPassword="", ; 管理员密码
ServerPassword="", ; 服务器密码
PublicPort=8211 ; 公共端口