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()
发表评论: