一个欲儿的博客

一个欲儿的博客

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

50f5a464959397c99142925a768560f2.png


image.png



饥荒存档启动

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()


发表评论: