Py做的饥荒联机版服务器辅助工具
2024-10-09


饥荒存档启动
import os
import shutil
import tkinter as tk
from tkinter import messagebox, filedialog
import subprocess
class DSServerManager:
def __init__(self, master):
self.master = master
master.title("Don't Starve Together 服务器管理器")
# 初始化路径
self.config_file_path = os.path.join(os.path.dirname(__file__), 'config.txt')
self.klei_profile = ""
self.server_path = ""
self.archive_folder = ""
# 读取配置文件
self.load_config()
# KLEI_PROFILE 显示框
self.klei_profile_label = tk.Label(master, text="本程序工作目录:")
self.klei_profile_label.pack()
self.klei_profile_entry = tk.Entry(master, width=50)
self.klei_profile_entry.pack()
self.klei_profile_button = tk.Button(master, text="选择工作目录", command=self.select_klei_profile)
self.klei_profile_button.pack()
# SERVER_PATH 显示框
self.server_path_label = tk.Label(master, text="服务器安装路径:")
self.server_path_label.pack()
self.server_path_entry = tk.Entry(master, width=50)
self.server_path_entry.pack()
self.server_path_button = tk.Button(master, text="选择服务器路径", command=self.select_server_path)
self.server_path_button.pack()
# 操作选择
self.choice_var = tk.StringVar(value="1")
self.move_radio = tk.Radiobutton(master, text="复制新的存档", variable=self.choice_var, value="1", command=self.update_archive_button)
self.move_radio.pack()
self.use_radio = tk.Radiobutton(master, text="使用已有的存档", variable=self.choice_var, value="2", command=self.update_archive_button)
self.use_radio.pack()
# 存档选择
self.archive_label = tk.Label(master, text="选择存档文件夹:")
self.archive_label.pack()
self.archive_listbox = tk.Listbox(master, height=6)
self.archive_listbox.pack()
self.archive_button = tk.Button(master, text="选择要复制的存档路径", command=self.select_archive)
self.archive_button.pack()
# 生成按钮
self.generate_button = tk.Button(master, text="生成并运行启动脚本", command=self.generate_script)
self.generate_button.pack()
# 将配置文件内容展示到文本框
self.update_text_entries()
def load_config(self):
"""从 config.txt 加载配置"""
if os.path.exists(self.config_file_path):
with open(self.config_file_path, 'r') as f:
lines = f.readlines()
if len(lines) >= 2:
self.klei_profile = lines[0].strip().split('=')[1] if '=' in lines[0] else ""
self.server_path = lines[1].strip().split('=')[1] if '=' in lines[1] else ""
else:
messagebox.showwarning("警告", "未找到配置文件,将要求手动选择路径。")
def save_config(self):
"""将当前路径保存到 config.txt 中"""
with open(self.config_file_path, 'w') as f:
f.write(f"KLEI_PROFILE={self.klei_profile}\n")
f.write(f"SERVER_PATH={self.server_path}\n")
def update_text_entries(self):
"""更新显示的路径"""
self.klei_profile_entry.delete(0, tk.END)
self.klei_profile_entry.insert(0, self.klei_profile)
self.server_path_entry.delete(0, tk.END)
self.server_path_entry.insert(0, self.server_path)
def select_klei_profile(self):
"""选择本程序工作目录"""
self.klei_profile = filedialog.askdirectory(title="选择本程序工作目录")
if self.klei_profile:
messagebox.showinfo("选择成功", f"本程序工作目录: {self.klei_profile}")
self.update_text_entries()
self.save_config()
def select_server_path(self):
"""选择服务器安装路径"""
self.server_path = filedialog.askdirectory(title="选择 Don't Starve Together Dedicated Server 安装路径")
if self.server_path:
messagebox.showinfo("选择成功", f"服务器路径: {self.server_path}")
self.update_text_entries()
self.save_config()
def update_archive_button(self):
"""根据用户选择的操作更新存档按钮文本"""
choice = self.choice_var.get()
if choice == '1':
self.archive_button.config(text="选择要复制的存档路径")
self.archive_listbox.delete(0, tk.END) # 清空列表框
else:
self.archive_button.config(text="更新存档列表")
self.load_existing_saves() # 加载已有存档列表
def load_existing_saves(self):
"""加载已有的存档文件夹到列表框"""
if not self.klei_profile:
messagebox.showerror("错误", "请先选择本程序工作目录")
return
existing_saves_path = os.path.join(self.klei_profile, "DoNotStarveTogether")
if not os.path.exists(existing_saves_path):
messagebox.showerror("错误", "没有找到 DoNotStarveTogether 文件夹")
return
self.archive_listbox.delete(0, tk.END) # 清空列表框
existing_saves = os.listdir(existing_saves_path)
for save in existing_saves:
if os.path.isdir(os.path.join(existing_saves_path, save)):
self.archive_listbox.insert(tk.END, save) # 添加存档到列表框
def select_archive(self):
"""选择存档路径"""
if self.choice_var.get() == '1': # 复制新的存档
self.archive_folder = filedialog.askdirectory(title="选择要复制的存档文件夹")
if self.archive_folder:
messagebox.showinfo("选择成功", f"要复制的存档路径: {self.archive_folder}")
else: # 使用已有的存档
selected_index = self.archive_listbox.curselection()
if selected_index:
selected_save = self.archive_listbox.get(selected_index)
self.archive_folder = os.path.join(self.klei_profile, "DoNotStarveTogether", selected_save)
messagebox.showinfo("选择成功", f"使用存档:{selected_save}")
else:
messagebox.showwarning("警告", "请从列表中选择一个存档。")
def generate_script(self):
"""生成启动脚本"""
if not self.klei_profile:
messagebox.showerror("错误", "请先选择本程序工作目录")
return
if not self.server_path:
messagebox.showerror("错误", "请先选择服务器路径")
return
dst_folder = os.path.join(self.klei_profile, "DoNotStarveTogether")
os.makedirs(dst_folder, exist_ok=True)
choice = self.choice_var.get()
selected_save = None
if choice == '1':
if not self.archive_folder:
messagebox.showerror("错误", "请先选择存档路径")
return
archive_name = os.path.basename(self.archive_folder)
target_path = os.path.join(dst_folder, archive_name)
if os.path.exists(target_path):
messagebox.showwarning("警告", f"目标路径已存在相同名称的存档:{archive_name}")
return
shutil.copytree(self.archive_folder, target_path)
messagebox.showinfo("成功", f"存档已复制到 {dst_folder}")
selected_save = archive_name
elif choice == '2':
selected_index = self.archive_listbox.curselection()
if selected_index:
selected_save = self.archive_listbox.get(selected_index)
else:
messagebox.showerror("错误", "没有找到任何存档文件夹")
return
if selected_save:
selected_save_path = os.path.join(dst_folder, selected_save)
master_exists = os.path.isdir(os.path.join(selected_save_path, 'Master'))
caves_exists = os.path.isdir(os.path.join(selected_save_path, 'Caves'))
# 检查并创建 cluster_token.txt
cluster_token_path = os.path.join(selected_save_path, 'cluster_token.txt')
if not os.path.exists(cluster_token_path):
with open(cluster_token_path, 'w') as token_file:
token_file.write("pds-g^KU_sM0lKedk^02YX0IWxmKF+B69D4jfjoTaiYcEVOMUV1CV21nge1zI=")
messagebox.showinfo("信息", f"已创建 cluster_token.txt 文件,并写入指定内容。")
bat_script = os.path.join(selected_save_path, "start_servers.bat")
with open(bat_script, 'w') as f:
f.write(f'@echo off\n')
f.write(f'set SteamAppId=322330\n')
f.write(f'set SteamGameId=322330\n')
f.write(f'set KLEI_PROFILE={self.klei_profile}\n')
if master_exists:
f.write(f'start "groundworld" /D "{self.server_path}\\bin" "{self.server_path}\\bin\\dontstarve_dedicated_server_nullrenderer.exe" -cluster {selected_save} -console -shard Master -persistent_storage_root "%KLEI_PROFILE%"\n')
else:
messagebox.showwarning("警告", f"存档 {selected_save} 没有 Master 文件夹,跳过 Master 启动命令。")
if caves_exists:
f.write(f'start "caveworld" /D "{self.server_path}\\bin" "{self.server_path}\\bin\\dontstarve_dedicated_server_nullrenderer.exe" -cluster {selected_save} -console -shard Caves -persistent_storage_root "%KLEI_PROFILE%"\n')
else:
messagebox.showwarning("警告", f"存档 {selected_save} 没有 Caves 文件夹,跳过 Caves 启动命令。")
# 运行生成的 .bat 文件
subprocess.Popen([bat_script], shell=True)
if __name__ == "__main__":
root = tk.Tk()
app = DSServerManager(root)
root.mainloop()饥荒联机版mod管理器
import os
import re
import chardet
import shutil
import tkinter as tk
from tkinter import ttk,scrolledtext
# 定义默认值
default_values = {'dstpath': "未设置路径", 'shoppath': "未设置路径", 'serverpath': "未设置路径"}
file_path = 'config.info'
# 检查文件是否存在
if os.path.exists(file_path):
# 文件存在,读取文件中的值
config = {}
with open(file_path, 'r') as file:
for line in file:
# 跳过空行
if not line.strip():
continue
try:
key, value = line.strip().split('=', 1) # 只分割一次
config[key] = value # 保持为字符串
except ValueError:
print(f"无法解析行: {line.strip()}") # 输出无法解析的行
else:
# 文件不存在,创建文件并写入默认值和示例路径
config = default_values
with open(file_path, 'w') as file:
for key, value in default_values.items():
file.write(f'{key}={value}\n')
# 写入示例路径
file.write("examledstpath=C:/app/Steam/steamapps/common/Don't Starve Together/mods\n")
file.write("examleshoppath=C:/app/Steam/steamapps/workshop/content/322330\n")
file.write("examleserverpath=C:/app/Steam/steamapps/common/Don't Starve Together Dedicated Server/mods\n")
# 重新读取文件
with open(file_path, 'r') as file:
config = {}
for line in file:
# 跳过空行
if not line.strip():
continue
try:
key, value = line.strip().split('=', 1)
config[key] = value
except ValueError:
print(f"无法解析行: {line.strip()}")
# 打印读取的配置值
dstpath = config.get('dstpath', "未设置路径").replace('\\', '/')
shoppath = config.get('shoppath', "未设置路径").replace('\\', '/')
serverpath = config.get('serverpath', "未设置路径").replace('\\', '/')
'''
print(dstpath)
print(shoppath)
print(serverpath)
'''
if dstpath == "未设置路径" or shoppath == "未设置路径" or serverpath == "未设置路径":
print("路径未配置\n请找到该目录下config.info\n用记事本打开并更改\n按照examlepath的格式即可(斜线用左斜线!!)\ndstpath是饥荒联机版的Mod路径\nshoppath是创意工坊的路径跟饥荒联机版在大致一致的位置\nserverpath是饥荒联机版开专服的软件的Mod路径")
while(True):
a = 1
'''
# 设置目录路径
dstpath = r"C:/app/Steam/steamapps/common/Don't Starve Together/mods"
shoppath = r"C:/app/Steam/steamapps/workshop/content/322330"
serverpath = r"C:/app/Steam/steamapps/common/Don't Starve Together Dedicated Server/mods"
'''
mods = []
def getmodname(file_path, char1, char2):
print(file_path)
pattern = r'name\s*=\s*"([^"]*)"'
# 尝试使用 UTF-8 编码读取文件
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
#print("使用 UTF-8 编码成功")
except UnicodeDecodeError:
print("UTF-8 编码失败,尝试检测文件编码...")
# 如果 UTF-8 读取失败,使用 chardet 进行编码检测
with open(file_path, 'rb') as file:
raw_data = file.read()
result = chardet.detect(raw_data)
print(result)
# 获取检测到的编码,如果检测失败,使用 chardet 检测结果
encoding_r = result['encoding']
if encoding_r is None:
#print("编码检测失败,使用默认编码 UTF-8")
encoding_r = 'utf-8'
# 按检测到或默认的编码读取文件内容
with open(file_path, 'r', encoding=encoding_r) as file:
content = file.read()
# 使用正则表达式查找匹配项
matches = re.findall(pattern, content)
if len(matches) == 0:
print("无法识别的mod")
while True:
a = 1
else:
return matches[0]
# 寻找完整的modinfo.lua路径
def getallfile(filepath):
for name in os.listdir(filepath):
path = os.path.join(filepath, name)
if os.path.isdir(path):
newpath = filepath + "/" + name
getallfile(newpath)
elif os.path.isfile(path):
if(name == "modinfo.lua"):
infopath = filepath + "/" + name
#print("path:" + infopath)
modname = getmodname(infopath,'name = "','"')
#print("modname:" + modname)
length = len(mods)
steamid = infopath.split("/")[-2:-1][0]
create_mod(str(length),steamid,filepath,modname)
# 向mods字典添加mod
def create_mod(num,steamid,path, modname):
mod = {"id":num,"steamid":steamid,"modpath":path,"modname":modname}
mods.append(mod)
#print(modname + "已添加入字典")
# 移动Mod到Serverfolder
def movebymyid(id):
for mod in mods:
if(mod['id'] == id):
modpath = mod['modpath']
steamid = mod['steamid']
modname = mod['modname']
if("workshop" in steamid):
dstserverpath = serverpath + "/" + steamid
cpmod(modpath,dstserverpath)
print(modname + " 已经成功转移!")
else:
dstserverpath = serverpath + "/workshop-" + steamid
cpmod(modpath,dstserverpath)
print(modname + " 已经成功转移!")
# 根据编号将server的mods删除
def clearbymyid(id):
for mod in mods:
if(mod['id'] == id):
modpath = mod['modpath']
rmmod(modpath)
#输出非ServerMods
def outmods(mods):
print("\n")
for mod in mods:
modpath = mod['modpath']
if(r"Don't Starve Together Dedicated Server" in modpath):
server = 1
else:
print("Mod编号:" + mod['id'])
print("SteamID:" + mod['steamid'])
print("Mod名字:" + mod['modname'])
print("Mod路径:"+ mod['modpath'])
print("\n")
#输出ServerMods
def outservermods(mods):
print("\n")
for mod in mods:
modpath = mod['modpath']
if(r"Don't Starve Together Dedicated Server" in modpath):
print("Mod编号:" + mod['id'])
print("SteamID:" + mod['steamid'])
print("Mod名字:" + mod['modname'])
print("Mod路径:"+ mod['modpath'])
print("\n")
#删除服务器所有Mods
def delallservermods(mods):
for mod in mods:
modpath = mod['modpath']
if(r"/Don't Starve Together Dedicated Server/" in modpath):
rmmod(modpath)
#自动添加所有Mods
def addallmods(mods):
for mod in mods:
modpath = mod['modpath']
if(r"/Don't Starve Together Dedicated Server/" in modpath):
server = 1
else:
modpath = mod['modpath']
steamid = mod['steamid']
modname = mod['modname']
if("workshop" in steamid):
dstserverpath = serverpath + "/" + steamid
cpmod(modpath,dstserverpath)
print(modname + " 已添加!")
else:
dstserverpath = serverpath + "/workshop-" + steamid
cpmod(modpath,dstserverpath)
print(modname + " 已添加!")
#复制Mod
def cpmod(src, dst):
if os.path.exists(dst):
shutil.rmtree(dst)
try:
shutil.copytree(src, dst)
except Exception as e:
print(f"复制目录时发生错误:{e}")
#删除Mod
def rmmod(modpath):
try:
shutil.rmtree(modpath)
#print(f"The folder '{modpath}' and all its contents have been deleted successfully.")
except OSError as e:
print(f"Error: {e.strerror}")
#刷新mods字典
def refreshmods():
mods.clear()
getallfile(dstpath)
getallfile(shoppath)
getallfile(serverpath)
refreshmods()
#outmods(mods)
'''
while(True):
ret = str(input("cin:"))
if(ret == "1"):#展示非服务器Mod
outmods(mods)
elif(ret == "2"):#单独移动某一个Mod
myid = str(input("请输入需要移动的Mod编号:"))
movebymyid(myid)
refreshmods()
elif(ret == "3"):#查看服务器下有哪些Mod
print("服务器已经包含的Mods:")
outservermods(mods)
elif(ret == "4"):#单独删除某一个Mod
myid = str(input("请输入需要删除的Mod编号:"))
clearbymyid(myid)
refreshmods()
elif(ret == "5"):#删除服务器下所有Mod
delallservermods(mods)
refreshmods()
elif(ret == "6"):#自动复制所有Mod到服务器下面去
addallmods(mods)
refreshmods()
'''
# 创建 Tkinter 窗口
root = tk.Tk()
root.title("饥荒联机版Mod管理器")
#root.geometry("1200x600")
# 定义按钮的回调函数
def display_outmods():
for row in tree_non_server.get_children():
tree_non_server.delete(row)
for mod in mods:
if "Don't Starve Together Dedicated Server" not in mod['modpath']:
tree_non_server.insert("", "end", values=(mod['modname'], mod['id'], mod['steamid'], mod['modpath']))
def display_servermods():
for row in tree_server.get_children():
tree_server.delete(row)
for mod in mods:
if "Don't Starve Together Dedicated Server" in mod['modpath']:
tree_server.insert("", "end", values=(mod['modname'], mod['id'], mod['steamid'], mod['modpath']))
def move_mod():
myid = entry.get()
movebymyid(myid)
refreshmods()
entry.delete(0, tk.END)
refresh_display()
def delete_mod():
myid = entry.get()
clearbymyid(myid)
refreshmods()
entry.delete(0, tk.END)
refresh_display()
def delete_all_server_mods():
delallservermods(mods)
refreshmods()
refresh_display()
def add_all_mods():
addallmods(mods)
refreshmods()
refresh_display()
def refresh_display():
display_outmods()
display_servermods()
# 创建按钮区域
button_frame = tk.Frame(root)
button_frame.pack(pady=10)
# 创建六个按钮
btn_outmods = tk.Button(button_frame, text="显示非Server下的Mod", command=display_outmods)
btn_outmods.grid(row=0, column=0, padx=10)
btn_outservermods = tk.Button(button_frame, text="显示Server下的Mod", command=display_servermods)
btn_outservermods.grid(row=0, column=1, padx=10)
btn_move_mod = tk.Button(button_frame, text="移动非Server下某个Mod到Server下", command=move_mod)
btn_move_mod.grid(row=0, column=2, padx=10)
btn_delete_mod = tk.Button(button_frame, text="删除Server下单个Mod", command=delete_mod)
btn_delete_mod.grid(row=0, column=3, padx=10)
btn_delete_all_server_mods = tk.Button(button_frame, text="删除所有Server下的所有Mod", command=delete_all_server_mods)
btn_delete_all_server_mods.grid(row=0, column=4, padx=10)
btn_add_all_mods = tk.Button(button_frame, text="一键移动非Server下的所有mod到Server下", command=add_all_mods)
btn_add_all_mods.grid(row=0, column=5, padx=10)
# 创建Treeview来显示非服务器mod信息
tree_frame_non_server = tk.Frame(root)
tree_frame_non_server.pack(pady=10)
# 非服务器 Mod 标签
label_non_server = tk.Label(tree_frame_non_server, text="非Server下Mod", font=("Arial", 12, "bold"))
label_non_server.pack()
columns = ("modname", "id", "steamid", "modpath")
tree_non_server = ttk.Treeview(tree_frame_non_server, columns=columns, show='headings', height=10)
tree_non_server.heading("modname", text="Mod Name")
tree_non_server.heading("id", text="Mod ID")
tree_non_server.heading("steamid", text="Steam ID")
tree_non_server.heading("modpath", text="Mod Path")
tree_non_server.column("modpath", width=400) # 直接给 modpath 列设宽度为400
tree_non_server.pack(fill=tk.BOTH, expand=True)
# 创建Treeview来显示服务器mod信息
tree_frame_server = tk.Frame(root)
tree_frame_server.pack(pady=10)
# 服务器 Mod 标签
label_server = tk.Label(tree_frame_server, text="Server下Mod", font=("Arial", 12, "bold"))
label_server.pack()
tree_server = ttk.Treeview(tree_frame_server, columns=columns, show='headings', height=10)
tree_server.heading("modname", text="Mod Name")
tree_server.heading("id", text="Mod ID")
tree_server.heading("steamid", text="Steam ID")
tree_server.heading("modpath", text="Mod Path")
tree_server.column("modpath", width=400) # 直接给 modpath 列设宽度为400
tree_server.pack(fill=tk.BOTH, expand=True)
# 创建输入框
entry_frame = tk.Frame(root)
entry_frame.pack(pady=10)
entry_label = tk.Label(entry_frame, text="请输入某个要操作的Mod ID:")
entry_label.pack(side=tk.LEFT)
entry = tk.Entry(entry_frame)
entry.pack(side=tk.LEFT)
root.mainloop()饥荒存档修改
import os
import tkinter as tk
from tkinter import messagebox
from tkinter import filedialog
# 重命名文件夹的函数
def rename_folders(base_path):
try:
for root, dirs, files in os.walk(base_path, topdown=True):
for dir_name in dirs:
if dir_name == "Caves":
os.rename(os.path.join(root, dir_name), os.path.join(root, "world1"))
elif dir_name == "Master":
os.rename(os.path.join(root, dir_name), os.path.join(root, "world0"))
break
messagebox.showinfo("成功", "存档修改完成!")
except Exception as e:
messagebox.showerror("错误", f"操作失败: {str(e)}")
# 选择文件夹的函数
def choose_directory():
path = filedialog.askdirectory() # 弹出文件夹选择框
if path:
entry_path.delete(0, tk.END) # 清空输入框
entry_path.insert(0, path) # 插入选择的路径
# 按钮点击的回调函数
def on_rename_click():
base_path = entry_path.get()
if os.path.isdir(base_path):
rename_folders(base_path)
else:
messagebox.showerror("错误", "请输入有效的文件夹路径!")
# 创建主窗口
root = tk.Tk()
root.title("饥荒联机版存档修改")
# 创建标签和路径输入框
label = tk.Label(root, text="选择存档路径:")
label.pack(pady=10)
entry_path = tk.Entry(root, width=50)
entry_path.pack(padx=10)
# 创建“选择路径”按钮
btn_choose = tk.Button(root, text="选择存档", command=choose_directory)
btn_choose.pack(pady=5)
# 创建“开始重命名”按钮
btn_rename = tk.Button(root, text="开始修改", command=on_rename_click)
btn_rename.pack(pady=10)
# 运行主循环
root.mainloop()
发表评论: