FMCRAFT MC Server 2026 年 上半年 更新公告

各位 FMCRAFT 的帅哥美女:

为优化服务器运行环境、提升全体玩家游戏体验,保障服务器稳定、安全、高质量运行,可爱的服主已于2026年上半年完成服务器全方位升级与优化调整。本次更新包含机房迁移、网络优化、版本升级、安全防护、bug修复及账号合规调整等多项核心内容,具体更新详情公告如下:

一、服务器机房与网络架构升级

  1. 完成主服务器机房迁移工作,现已正式搬迁至安徽芜湖机房,有效优化区域网络延迟,提升服务器运行稳定性。
  2. 独立拆分登录凭据服务器,将其迁移至中国杭州优质机房,优化玩家登录响应速度,减少登录卡顿、失败等问题。
  3. 优化内网网络部署,针对服务器部分内网线路启用SakuraFRP内网穿透服务,进一步优化网络传输链路,改善联机网络环境。

二、游戏版本升级

本次已正式将服务器游戏版本升级至 Minecraft 1.21.11,同步适配新版本核心玩法、内容特性及兼容性优化,玩家可体验新版本全新游戏内容。

三、安全防护体系升级

为严厉打击外挂、违规作弊行为,维护公平公正的游戏环境,服务器正式接入Matrix反作弊系统,全方位监测游戏违规行为,强力拦截各类作弊操作,守护正常玩家游戏体验。

四、问题修复与体验优化

针对前期服务器运行过程中出现的各类已知bug、卡顿故障、功能异常等问题进行全面排查与修复,同时优化服务器运行机制、资源调度及玩家交互体验,大幅提升游戏流畅度与服务器稳定性。

五、账号合规整治调整

依据服务器用户公约及游戏违规规则,完成新一轮账号合规核查处理:对多次违规、破坏服务器游戏环境、扰乱社区秩序的违规账号进行永久/限时封禁;同时对前期误封、违规情节已肃清的合规玩家账号予以解封,恢复正常游戏权限。

本次更新旨在为各位玩家提供更稳定、安全、优质的游戏服务,后续我们将持续跟进服务器运行状态,定期优化更新、修复问题。欢迎各位玩家正常上线体验新版本内容,自觉遵守服务器规则,共同维护良好的游戏社区环境。

六、其他

预祝各位生活愉快。

该退服的赶紧退。

https://f.wps.cn/g/K0Hr27v5/【WPS表单】邀你填写「FMCRAFT 2026 年 上半年 综合调查问卷」

我 - TropicalFish

该篇文章由 AI 生成

论坛用户注册及使用协议

第一条 总则

  1. 本协议为本站(以下简称“论坛”)与注册用户(以下简称“您”)之间的服务约定,依据《中华人民共和国网络安全法》《个人信息保护法》《民法典》等法律法规制定。
  2. 您完成注册、勾选同意并登录使用论坛,即表示自愿接受本协议全部约束;若不同意任一条款,请立即终止注册。
  3. 论坛定位为校园交流平台,旨在营造文明、理性的沟通氛围,所有用户均需遵守国家法律、社会公序良俗以及校园管理规定。

第二条 账号与信息规则

  1. 注册账号仅设置自定义昵称,禁止使用校牌编号、学号、真实姓名作为昵称/账号,从源头保护个人信息。
  2. 您自愿授权站内其他注册用户可通过@昵称的形式与您进行正常交流、客观评价与讨论;您知晓并接受该站内互动形式带来的正常社交影响。
  3. 全体用户绝对禁止以任何形式发布、转发他人校牌ID、学号、住址、社交账号、生活隐私等敏感个人信息。
  4. 您需妥善保管账号密码,因账号泄露、转借产生的一切后果,由账号本人自行承担。

第三条 发帖与言行规范(核心禁令)

  1. 允许内容:客观陈述事实、正常观点交流、生活分享、友好讨论,发言需保持尊重。
  2. 严格禁止以下行为,一经发现,管理员有权直接删帖、警告、限制或永久封禁账号:

    • 发布脏话、辱骂、人身攻击、恶意起外号、嘲讽、恐吓他人;
    • 捏造事实、造谣诽谤、歪曲真相,损害他人名誉;
    • 曝光他人隐私、人肉搜索、定向挂人、煽动网友孤立他人;
    • 发布低俗、违规、违反法律法规及校园纪律的内容;
    • 利用@昵称功能反复刷屏、恶意骚扰、集体围攻他人。
  3. 针对他人行为、事件的评价,仅限客观描述,不得进行人格贬低、恶意定性。

第四条 责任划分

  1. 谁发帖,谁负责:您在站内发布的所有文字、内容,对应的法律责任、纪律责任均由您本人独立承担。
  2. 论坛管理员仅负责内容管理、违规处置,不对用户自主发布的言论承担连带责任。
  3. 若您发现他人违规发言、侵权行为,可通过站内举报渠道反馈,管理员将及时核查处理。

第五条 信息保护

  1. 论坛不会主动收集、留存用户校牌、学号、真实身份等校外信息,仅保留账号登录必要数据。
  2. 非经法律机关依法要求,论坛不会向任何第三方泄露用户账号及使用记录。

第六条 附则

  1. 本协议论坛有权根据运营及法规要求适时更新,更新后继续使用即视为认可新条款。
  2. 因使用本论坛产生的一切纠纷,适用中华人民共和国法律。

第七条 灌水区发帖规则

  1. 友好交流,理性发言,禁止辱骂、人身攻击、恶意骚扰、造谣抹黑。
  2. 仅允许@论坛自定义昵称互动,严禁出现任何人的校牌、学号、真实姓名、隐私信息。
  3. 客观讨论他人行为、事件即可,不允许刻意挂人、煽动对立。
  4. 违规内容一律删除,多次违规将封禁账号,请大家共同维护交流环境。

第八条 保留向学校举报行为

  1. 若出现严重违法学校校纪校规行为,我们会向学校相关管理人员举报。

结果公示【FMCRAFT 2026 年 5 月 新插件投票 & 管理选举大会】

前言

本次投票隐藏投票人。

表格顺序为递增时间。

红色单元格为无效填写,例如:“人好”、“这人谁啊”。总结结论是按过滤无效答案后的结果。

是否给予奖励列 决定了您是否有奖励,请自行认领表单并联系服主获取奖励;如您该单元格包含“给予处罚”,需要承担相应的警告、封禁。

本次投票人数并不是很多,后期继续观察。

新插件投票

结果公示

新插件.png

总结

安装 GSit、AntiPopup、Villager Market。

请注意,安装了不一定会使用,也可能会因为不会调试而删除,具体请查看相关官方社群。

管理员选举

结果公示

管理员

总结

全部保留、不选举新管理。

本轮管理员选举为 AI 客观选举,将服务器部分日志作为知识库令 AI 判断。

故全部保留。

FMCRAFT 2026年5月 公开注册公告

亲爱的玩家:

为迎接更多新玩家加入FMCRAFT我的世界服务器,体验纯正的多人联机生存乐趣,服务器将于2026年5月开启限时公开注册活动,本次注册无需邀请码,具体规则与时间安排如下:

一、注册开放时间

  • 开放开始时间:2026-05-02 00:00:01
  • 开放结束时间:2026-05-05 23:59:59

注:上述时间窗口内,服务器将自动开启公开免费注册,无需任何邀请码;时间窗口外,服务器将恢复常规邀请制注册,未持有有效邀请码的玩家无法完成注册。

二、注册通道(两种方式任选)

  1. 网页端注册
    访问官方注册地址:https://user.fmcraft.top/auth/register.php,按页面提示填写账号、密码及相关信息,完成验证即可注册成功。
  2. 游戏内注册
    直接进入FMCRAFT服务器,在游戏内按照指引输入注册指令,完成账号创建与绑定。

三、重要规则说明

  1. 邀请码绑定规则
    本次免费注册的账号,可在注册后的任意时间,绑定其他玩家的邀请码,绑定后可享受邀请码对应的专属福利与权限。
  2. 账号清理规则
    为保障服务器资源合理分配与生态健康,未绑定邀请码、且长期未登录的账号,服务器有权无理由予以封禁清理,请各位玩家及时绑定邀请码并保持账号活跃。
  3. 账号安全须知
    请务必填写真实有效的注册信息,妥善保管账号密码,严禁账号交易、共享、转借等行为,违者一经查实将永久封禁账号。

四、温馨提示

  • 本次公开注册为限时活动,逾期将关闭通道,恢复邀请制,请有意向的玩家把握注册时机。
  • 注册过程中如遇问题,可通过官方交流群联系管理员协助处理。
  • 服务器严禁使用外挂、作弊模组、恶意破坏等违规行为,违者将严厉处罚,共同维护公平友好的游戏环境。

期待各位新玩家加入FMCRAFT,一起开启精彩的MC冒险之旅!

FMCRAFT
2026年4月27日

FMCRAFT 2026 年 5 月 新插件投票 & 管理选举大会

前言

基于现有发展状况,我们决定在 2026 年 4 月 26 日 至 5 月 5 日发布《FMCRAFT 2026 年 5 月 新插件投票 & 管理选举大会》相关投票。

新插件投票

关于

我们在 Spigot | SpigotMC - High Performance Minecraft Software 选择了一些可能适用于服务器的新插件。

我们会根据投票结果,进行新插件的安装。

投票完成之后,会进行测试,再进行第二轮投票。

警告整理,内容如下:

LevelledMobs

功能:给怪物加上RPG等级系统,怪物越强血量/攻击越高,掉落也更丰厚,生存更有挑战性!
插件链接:LevelledMobs | SpigotMC - High Performance Minecraft Software

Magic

功能:超酷魔法插件,魔杖、法术、技能、魔法装备全都有,秒变魔法RPG服!
插件链接:Magic | SpigotMC - High Performance Minecraft Software

GSit

功能:可以坐下、躺平、爬行、叠坐互动,主城聊天、拍照、休闲超好用!
插件链接:[GSit - Modern Sit (Seat and Chair), Lay and Crawl Plugin! - [1.16 - 26.1.2] | SpigotMC - High Performance Minecraft Software](https://www.spigotmc.org/resources/gsit-modern-sit-seat-and-chair-lay-and-crawl-plugin-1-16-26-1-2.62325/)

AntiPopup

功能:屏蔽聊天举报和烦人的系统弹窗,纯净游戏体验,不弹乱七八糟提示!
插件链接:✨ AntiPopup - No chat reports and Popup! ✨ | SpigotMC - High Performance Minecraft Software

Villager Market

功能:玩家商店系统,自己开村民店铺,买卖物品、做生意、搞经济超方便!
插件链接:Villager Market | The ultimate shop plugin | SpigotMC - High Performance Minecraft Software

投票链接

FMCRAFT 2026 年 5 月 新插件投票 – 填写表单

如果您的答卷理由充分、选择客观,您可以获得对应的积分奖励。

一旦发现刷票行为给予警告/封禁。

管理选举大会

关于

字面意思。

目前的管理员名单及选择理由:

管理员理由
121230指令
WinucAlan特别选择
kekele历届好评高、管理能力强
langke1145历届好评高、管理能力强
zlejdu有一定的运维经验

投票中,您可以选择撤下某位管理的权限或保持某位管理的权限,但是需要提供理由(如果选择了保持,可以复制上面的理由)。

额外的,您可以额外选择至多 3 位玩家成为管理,不允许自己,但是需要提供理由。

投票链接

FMCRAFT 2026 年 5 月 管理选举大会 – 填写表单

如果您的答卷理由充分、选择客观,您可以获得对应的积分奖励。

一旦发现刷票行为给予警告/封禁。

m3u8 下载器

简介

一款m3u8批量下载的软件,支持部分分分加密的m3u8文件,有些加密的应该是没办法搞定

当复制到链接的时候会自动粘贴进入,不需要自己手动点复制粘贴

其它信息如图,自己测试了还能用,不知道其他老铁们能不能正常用,有兴趣的可以源码查看修改.....

不喜勿喷

关于为什么要从吾爱复制这个东西,因为觉得这个东西是真的有用,然而有时又可能找不到

图片

mnvso78g.png

源代码

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import threading
import queue
import os
import time
import requests
import m3u8
from concurrent.futures import ThreadPoolExecutor
from urllib.parse import urljoin
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
 
# -------------------------- 配置项 --------------------------
DEFAULT_THREAD_NUM = 8
TIMEOUT = 15
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows 10; Win64; x64) AppleWebKit/537.36"
}
DEFAULT_PATH = os.path.join(os.path.expanduser("~"), "Desktop")
 
 
# ------------------------------------------------------------
 
class M3U8Downloader(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("M3U8批量下载器V0.1  By豪哥")
        self.geometry("570x450")
        self.resizable(False, False)
 
        # 全局变量
        self.save_path = tk.StringVar(value=DEFAULT_PATH)
        self.total_ts = 0
        self.finish_ts = 0
        self.msg_queue = queue.Queue()
        self.is_downloading = False
        self.key = None
        self.iv = None
        self.last_clipboard = ""
        self.thread_num = tk.IntVar(value=DEFAULT_THREAD_NUM)
        self.last_link = ""
 
        # 下载计时&速度统计
        self.start_time = 0
        self.total_downloaded_bytes = 0
 
        # 批量下载变量
        self.video_list = []
        self.current_video_idx = 0
        self.total_videos = 0
        self.remaining_videos = 0  # 剩余视频数量
        self.video_size = 0.00
 
        self.init_ui()
        self.check_queue()
        self.watch_clipboard()
        self.link_text.bind("<<Modified>>", self.on_link_changed)
 
    def init_ui(self):
        """初始化界面"""
        # ====================== 顶部按钮区域 ======================
        top_frame = tk.Frame(self, pady=5)
        top_frame.pack(fill=tk.X, padx=10)
 
        self.btn_path = tk.Button(
            top_frame, text="&#128194; 下载目录", command=self.select_save_path,
            width=12, font=("微软雅黑", 9)
        )
        self.btn_path.pack(side=tk.LEFT)
 
        self.label_path = tk.Label(
            top_frame, textvariable=self.save_path, fg="blue", font=("微软雅黑", 9)
        )
        self.label_path.pack(side=tk.LEFT, padx=5)
 
        self.btn_start = tk.Button(
            top_frame, text="&#9654;&#65039; 开始下载", command=self.start_batch_download,
            width=12, font=("微软雅黑", 9)
        )
        self.btn_start.pack(side=tk.RIGHT)
 
        # ====================== 进度条+百分比 ======================
        progress_frame = tk.Frame(self, pady=3)
        progress_frame.pack(fill=tk.X, padx=10)
        tk.Label(progress_frame, text="下载进度:", font=("微软雅黑", 9)).pack(side=tk.LEFT)
 
        self.progress_bar = ttk.Progressbar(progress_frame, mode="determinate", length=380)
        self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
 
        self.progress_label = tk.Label(progress_frame, text="0%", font=("微软雅黑", 9))
        self.progress_label.pack(side=tk.LEFT)
 
        # ====================== M3U8链接列表 + 线程选择 ======================
        link_frame = tk.Frame(self, pady=5)
        link_frame.pack(fill=tk.X, padx=10)
        tk.Label(link_frame, text="M3U8链接列表(每行一个):", font=("微软雅黑", 9)).pack(anchor=tk.W)
 
        self.link_text = tk.Text(link_frame, height=4, font=("微软雅黑", 9))
        self.link_text.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.link_text.edit_modified(False)
 
        # 右侧:线程数选择
        thread_frame = tk.Frame(link_frame)
        thread_frame.pack(side=tk.RIGHT, padx=5)
 
        tk.Label(thread_frame, text="下载线程:", font=("微软雅黑", 9)).pack(anchor=tk.N)
        self.thread_combobox = ttk.Combobox(
            thread_frame, textvariable=self.thread_num,
            values=[1, 2, 4, 8, 16, 32], state="readonly", width=5
        )
        self.thread_combobox.current(3)
        self.thread_combobox.pack(anchor=tk.N)
 
        self.btn_clear = tk.Button(
            thread_frame, text="清空", command=self.clear_link,
            width=6, font=("微软雅黑", 9)
        )
        self.btn_clear.pack(anchor=tk.N, pady=5)
 
        # ====================== 左右分栏布局 ======================
        bottom_frame = tk.Frame(self, pady=5)
        bottom_frame.pack(fill=tk.BOTH, expand=True, padx=10)
 
        # 左侧:视频统计信息
        self.stats_frame = tk.LabelFrame(bottom_frame, text="视频统计信息", font=("微软雅黑", 9), width=180)
        self.stats_frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=(0, 5))
        self.stats_frame.pack_propagate(False)
 
        # ========== 【总视频 + 剩余视频】 ==========
        self.label_total_videos = tk.Label(self.stats_frame, text=f"总视频数:0", font=("微软雅黑", 8))
        self.label_total_videos.pack(anchor=tk.W, padx=5, pady=2)
 
        self.label_remaining_videos = tk.Label(self.stats_frame, text=f"剩余视频数:0", font=("微软雅黑", 8))
        self.label_remaining_videos.pack(anchor=tk.W, padx=5, pady=2)
        # ==========================================
 
        # 视频大小
        self.label_size = tk.Label(self.stats_frame, text=f"大小:{self.video_size:.2f} MB", font=("微软雅黑", 8))
        self.label_size.pack(anchor=tk.W, padx=5, pady=2)
        # TS总数/已下载
        self.label_ts_info = tk.Label(self.stats_frame, text=f"TS总数:{self.total_ts}/{self.finish_ts}", font=("微软雅黑", 8))
        self.label_ts_info.pack(anchor=tk.W, padx=5, pady=2)
        # 总下载速度
        self.label_speed = tk.Label(self.stats_frame, text="总速度:0.00 MB/s", font=("微软雅黑", 8))
        self.label_speed.pack(anchor=tk.W, padx=5, pady=2)
        # 已耗时
        self.label_cost = tk.Label(self.stats_frame, text="已耗时:00:00", font=("微软雅黑", 8))
        self.label_cost.pack(anchor=tk.W, padx=5, pady=2)
        # 预估剩余时间
        self.label_remain = tk.Label(self.stats_frame, text="剩余时间:--:--", font=("微软雅黑", 8))
        self.label_remain.pack(anchor=tk.W, padx=5, pady=2)
 
        # 右侧:运行日志
        log_frame = tk.LabelFrame(bottom_frame, text="运行日志", font=("微软雅黑", 9))
        log_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
 
        log_scroll = tk.Scrollbar(log_frame)
        log_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.log_text = tk.Text(
            log_frame, height=5, font=("微软雅黑", 8),
            yscrollcommand=log_scroll.set, state=tk.DISABLED
        )
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        log_scroll.config(command=self.log_text.yview)
 
    def time_format(self, seconds):
        """秒数转换为 分:秒 格式"""
        if seconds < 0:
            return "00:00"
        m = int(seconds // 60)
        s = int(seconds % 60)
        return f"{m:02d}:{s:02d}"
 
    def on_link_changed(self, event):
        """链接框内容变化 → 自动更新总视频数"""
        self.link_text.edit_modified(False)
        links = self.link_text.get(1.0, tk.END).strip().splitlines()
        valid_links = [l.strip() for l in links if l.strip().startswith("http")]
 
        # 自动统计视频数量
        self.total_videos = len(valid_links)
        self.remaining_videos = self.total_videos
        self.update_video_count_labels()
 
        if self.is_downloading:
            return
 
        first_link = next((l.strip() for l in valid_links), "")
        if first_link == self.last_link:
            return
        self.last_link = first_link
        if not first_link:
            self.reset_stats()
            return
        threading.Thread(target=self.pre_parse_m3u8_info, args=(first_link,), daemon=True).start()
 
    def update_video_count_labels(self):
        """更新总视频数 / 剩余视频数"""
        self.label_total_videos.config(text=f"总视频数:{self.total_videos}")
        self.label_remaining_videos.config(text=f"剩余视频数:{self.remaining_videos}")
 
    def pre_parse_m3u8_info(self, m3u8_url):
        """预解析M3U8信息"""
        try:
            m3u8_obj = m3u8.load(m3u8_url, headers=HEADERS, timeout=TIMEOUT)
            temp_size = 0.0
            if m3u8_obj.playlists:
                new_url = urljoin(m3u8_url, m3u8_obj.playlists[-1].uri)
                m3u8_obj = m3u8.load(new_url, headers=HEADERS, timeout=TIMEOUT)
            temp_total_ts = len(m3u8_obj.segments)
            for seg in m3u8_obj.segments:
                temp_size += seg.duration * 0.5
            self.video_size = temp_size
            self.total_ts = temp_total_ts
            self.finish_ts = 0
            self.update_stats()
        except Exception as e:
            self.reset_stats()
 
    def reset_stats(self):
        """重置统计信息"""
        self.video_size = 0.00
        self.total_ts = 0
        self.finish_ts = 0
        self.total_downloaded_bytes = 0
        self.start_time = 0
        self.update_stats()
 
    def watch_clipboard(self):
        """监听剪贴板,自动换行添加链接"""
        try:
            content = self.clipboard_get().strip()
            if content != self.last_clipboard and content.startswith("http") and ("m3u8" in content):
                self.last_clipboard = content
                self.link_text.insert(tk.END, content + "\n")
        except:
            pass
        self.after(1000, self.watch_clipboard)
 
    def select_save_path(self):
        path = filedialog.askdirectory(title="选择保存目录", initialdir=DEFAULT_PATH)
        if path:
            self.save_path.set(path)
 
    def clear_link(self):
        self.link_text.delete(1.0, tk.END)
        self.total_videos = 0
        self.remaining_videos = 0
        self.update_video_count_labels()
        self.reset_stats()
 
    def write_log(self, msg):
        self.msg_queue.put(("log", msg))
 
    def update_progress(self):
        self.msg_queue.put(("progress", None))
 
    def update_stats(self):
        self.msg_queue.put(("stats", None))
 
    def download_finish(self):
        self.msg_queue.put(("finish", None))
 
    def check_queue(self):
        """刷新GUI(线程安全)"""
        while not self.msg_queue.empty():
            task, data = self.msg_queue.get()
            if task == "log":
                self.log_text.config(state=tk.NORMAL)
                self.log_text.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {data}\n")
                self.log_text.config(state=tk.DISABLED)
                self.log_text.see(tk.END)
            elif task == "progress":
                percent = int(self.finish_ts / self.total_ts * 100) if self.total_ts > 0 else 0
                self.progress_bar["value"] = self.finish_ts
                self.progress_label.config(text=f"{percent}%")
                self.label_ts_info.config(text=f"TS总数:{self.total_ts}/{self.finish_ts}")
                if self.finish_ts > 0 and self.start_time > 0:
                    cost_time = time.time() - self.start_time
                    speed_mb = (self.total_downloaded_bytes / 1024 / 1024) / cost_time if cost_time > 0 else 0
                    remain_ts = self.total_ts - self.finish_ts
                    remain_time = (remain_ts * cost_time) / self.finish_ts if self.finish_ts > 0 else 0
                    self.label_speed.config(text=f"总速度:{speed_mb:.2f} MB/s")
                    self.label_cost.config(text=f"已耗时:{self.time_format(cost_time)}")
                    self.label_remain.config(text=f"剩余时间:{self.time_format(remain_time)}")
            elif task == "stats":
                self.label_size.config(text=f"大小:{self.video_size:.2f} MB")
                self.label_ts_info.config(text=f"TS总数:{self.total_ts}/{self.finish_ts}")
                self.label_speed.config(text="总速度:0.00 MB/s")
                self.label_cost.config(text="已耗时:00:00")
                self.label_remain.config(text="剩余时间:--:--")
            elif task == "finish":
                self.is_downloading = False
                self.btn_start.config(state=tk.NORMAL, text="&#9654;&#65039; 开始下载")
                self.progress_label.config(text="0%")
        self.after(50, self.check_queue)
 
    def get_decrypt_key(self, key_uri, base_url):
        try:
            key_url = urljoin(base_url, key_uri)
            res = requests.get(key_url, headers=HEADERS, timeout=TIMEOUT)
            res.raise_for_status()
            return res.content
        except:
            return None
 
    def decrypt_ts(self, ts_data):
        if self.key is None:
            return ts_data
        try:
            cipher = AES.new(self.key, AES.MODE_CBC, self.iv or self.key)
            return unpad(cipher.decrypt(ts_data), AES.block_size)
        except:
            return ts_data
 
    def download_ts(self, ts_url, save_dir, index):
        """下载单个TS分片"""
        ts_name = f"{index:05d}.ts"
        ts_path = os.path.join(save_dir, ts_name)
        try:
            with requests.get(ts_url, headers=HEADERS, timeout=TIMEOUT, stream=True) as r:
                r.raise_for_status()
                ts_data = self.decrypt_ts(r.content)
                self.total_downloaded_bytes += len(ts_data)
                with open(ts_path, "wb") as f:
                    f.write(ts_data)
            self.write_log(f"下载:{ts_name}")
            self.finish_ts += 1
            self.update_progress()
        except Exception as e:
            self.write_log(f"失败:{ts_name}")
 
    def merge_ts(self, ts_dir, save_path):
        video_name = time.strftime("%Y%m%d_%H%M%S") + ".mp4"
        video_path = os.path.join(save_path, video_name)
        ts_files = sorted([f for f in os.listdir(ts_dir) if f.endswith(".ts")])
        with open(video_path, "wb") as out:
            for ts in ts_files:
                with open(os.path.join(ts_dir, ts), "rb") as f:
                    out.write(f.read())
        for ts in ts_files:
            os.remove(os.path.join(ts_dir, ts))
        os.rmdir(ts_dir)
        self.write_log(f"&#9989; 下载完成!文件地址:{video_path}")
 
    def parse_m3u8(self, m3u8_url):
        m3u8_obj = m3u8.load(m3u8_url, headers=HEADERS, timeout=TIMEOUT)
        self.video_size = 0.0
        if m3u8_obj.playlists:
            new_url = urljoin(m3u8_url, m3u8_obj.playlists[-1].uri)
            return self.parse_m3u8(new_url)
        self.total_ts = len(m3u8_obj.segments)
        for seg in m3u8_obj.segments:
            self.video_size += seg.duration * 0.5
        self.key = None
        self.iv = None
        if m3u8_obj.keys and m3u8_obj.keys[0]:
            key_info = m3u8_obj.keys[0]
            self.key = self.get_decrypt_key(key_info.uri, m3u8_url)
            self.iv = bytes.fromhex(key_info.iv[2:]) if key_info.iv else None
        return m3u8_obj, m3u8_url.rsplit("/", 1)[0] + "/"
 
    def download_single_video(self, m3u8_url):
        """下载单个视频"""
        try:
            self.start_time = time.time()
            self.total_downloaded_bytes = 0
            self.write_log(f"&#127916; 开始下载第 {self.current_video_idx} 个视频")
            m3u8_obj, base_url = self.parse_m3u8(m3u8_url)
            self.update_stats()
            if not m3u8_obj.segments:
                self.write_log("&#10060; 未找到视频分片!")
                return
            self.finish_ts = 0
            self.progress_bar["maximum"] = self.total_ts
            self.progress_bar["value"] = 0
            self.write_log(f"解析完成!总TS数:{self.total_ts}")
            ts_dir = os.path.join(self.save_path.get(), "temp_ts")
            os.makedirs(ts_dir, exist_ok=True)
            thread_count = self.thread_num.get()
            self.write_log(f"&#128640; 启动 {thread_count} 线程下载...")
            with ThreadPoolExecutor(thread_count) as executor:
                for idx, url in enumerate([urljoin(base_url, seg.uri) for seg in m3u8_obj.segments]):
                    executor.submit(self.download_ts, url, ts_dir, idx)
            self.merge_ts(ts_dir, self.save_path.get())
        except Exception as e:
            self.write_log(f"&#128165; 下载异常:{str(e)}")
 
    def batch_download_worker(self):
        """批量下载主逻辑"""
        try:
            links = self.link_text.get(1.0, tk.END).strip().splitlines()
            self.video_list = [link.strip() for link in links if link.strip().startswith("http")]
            self.total_videos = len(self.video_list)
            self.remaining_videos = self.total_videos
            self.current_video_idx = 0
            self.update_video_count_labels()
 
            if self.total_videos == 0:
                messagebox.showerror("错误", "请输入有效的M3U8链接!")
                return
 
            self.write_log(f"&#128229; 共加载 {self.total_videos} 个视频,开始批量下载")
 
            for url in self.video_list:
                self.current_video_idx += 1
                # ========== 【核心修改:开始下载前就 -1】 ==========
                self.remaining_videos -= 1
                self.update_video_count_labels()
                # ================================================
                self.download_single_video(url)
 
            self.write_log("&#127881; 所有视频下载完成!")
            messagebox.showinfo("完成", "全部视频下载成功!")
            self.clear_link()
 
        except Exception as e:
            self.write_log(f"批量下载异常:{str(e)}")
        finally:
            self.download_finish()
 
    def start_batch_download(self):
        """启动批量下载"""
        if self.is_downloading:
            messagebox.showwarning("提示", "正在下载中!")
            return
        self.is_downloading = True
        self.btn_start.config(state=tk.DISABLED, text="&#9208; 下载中...")
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
        threading.Thread(target=self.batch_download_worker, daemon=True).start()
 
 
if __name__ == "__main__":
    app = M3U8Downloader()
    app.mainloop()

我已经几年都没写文章了,今天来写一篇罢。

Main: 公开课

概述

公开课我觉得就是一个傻逼玩意儿
每次想到这个逼东西我就想吐,并非心理上,而是生理上,胃爆炸的了那种

为什么?
演戏罢了
全班演戏,老师演戏,校长演戏,连来听课的那几个傻逼也在演戏
所有人都知道自己在演,但所有人都装作没在演
你说这不是有病是什么

课前

傻逼老师:”同学们,明天的公开课很重要,关系到学校的评级“
好吧,开始傻逼了
班主任站在讲台上面,慷慨激昂,似乎叫我们去打仗
说难听点,班主任说的这些话让我认为好像这节课不上学校就会倒闭😂

他花了长达 20min 的时间布置任务
例如:

  • 谁坐哪
  • 谁在什么时候举手
  • 谁在门口喊“老师好”
  • 谁回答什么问题
  • 用哪只手举手

很明显,是个剧本
全部都安排的明明白白

  • “记住了吗?”
  • “记住了————”

好吧,看起来就是长音姐发力了
全班齐声回答,声音大到傻逼特朗普都听到了

平时上课我问你“记住了吗”你他妈屁都不放一个,现在倒是挺齐的啊?贱不贱啊?

最搞笑的是她说了一句:“大家一定要自然一点,千万不要像排练过一样。”
自然你大坝
他妈的蛋的你 让我背十遍答案,让我自然?
何意味
当我傻逼?

课时

早上一进教室,我还以为是我走错了教室
黑板擦得跟镜子似的,反光
讲台上摆了一盆花,假的
后面板报重新贴了一遍,颜色鲜艳得像他妈幼儿园
这种摆弄有什么意思呢?
应付检查?

班主任站在门口,穿着那件只有过年才穿的红外套,笑得跟朵菊花似的
“欢迎欢迎,老师们辛苦了——”
依旧震碎特朗普的耳朵
平时骂我们的时候,声音可不是这样的
听课的老师进来了,三个人,都戴着眼镜,手里拿着本子,一脸“我是专家”的表情
我演,你演,他演

开课了

“同学们好——“
“老师好————”

我操,这声音,比合唱团还正宗,平时这声”老师好“乱的要命,像一团死尸似的;然而今天不一样

老师开始提问了
第一个问题,照旧,课代表举手,标准姿势,五指并拢,手肘贴耳,回答的一字不差。废话,他昨天背了一晚上
突然,有一个人没按预期计划举手了... 老师给了他一个眼神,然后就放下了
第二个问题,举手
第三个问题,举手
全是安排好的
平时上课老师问个问题,全班低头,像集体猝死了一样;今天倒好,一个个抢着举手,跟直播间抢优惠券似的
我看着他们,觉得恶心
不是骂他们,是觉得这个场面恶心

所有人都在这演戏,演给那三个戴眼镜的傻逼看,那三个傻逼还真的在点头,还在本子上写东西,好像真的学到了什么了不起的教学方法一样
学你大坝
这场戏给谁看的?我们又学到了什么?学到了个几把
真实的,任何人都知道

好笑的

老师要写板书了。
她拿起黑板擦——一个新的,还包着塑料纸。
她愣了一秒。
然后她把那个新的放下,从抽屉里拿出旧的。旧的上面全是粉笔灰,灰扑扑的,边上缺了个角。
她用旧的擦了黑板。
粉笔灰落下来,落在她的袖口上。
那一刻我忽然觉得,这是整堂公开课里,唯一真实的东西。
但也就一秒。
下一秒她又笑了,又开始演了。

我并不是喜欢加标点符号,但是这里加了标点符号

课后

下课铃响了

  • “同学们再见”
  • “老师再见——————————”

合唱团 + 14514

听课老师站起来,和班主任握手 “这节课上得很好,学生状态特别棒”
班主任笑得跟花一样:“哪里哪里,还有很多不足”

不足你妈,你明明就是在等这句话

他们走了
关上门的那一刻,教室的气氛就变了

班主任的脸,垮了,声音也变了
“今天表现还行”她说,“但是——明天随堂测验,别以为公开课上完了就没事”

我就知道
这是肯定的
表演结束

最后

第二天,那个新的黑板擦被放回了抽屉

旧的又躺回了黑板槽里,灰扑扑的,上面还有昨天的粉笔灰

没人再碰它

如何在 FMCRAFT 中使用语音通话?

前置条件

装 simplevoicechat 插件。

支持 Fabric 和 Forge。

如果不会的话自己询问服务器的其他人。

下载 simplevoicechat 方法

一 启动器下载

二 自行下载https://pic.fmcraft.top/file/hjldg01o.jar

如何配置

进服,按 v 打开配置。

点击设置。

将 按键通话 设置为 音量触发。

不用的时候请关闭麦克风和扬声器。

同时,你也可以点击群组,创建一个群组,来邀请好友。

【繁忙的工艺 FMCRAFT】服务器解封与申诉规则

一、总则

  1. 目的:本规则旨在为受到处罚的玩家提供公正的申诉渠道,明确解封条件与流程,确保服务器环境的公平、有序。
  2. 适用范围:适用于所有因违反《服务器许可条款》而被处以“旁观者模式”、“禁言”、“暂时封禁”或“永久封禁”的玩家账号。
  3. 法律依据:本规则建立在《中华人民共和国网络安全法》及原《服务器许可条款》基础之上。若申诉内容涉及违法(如黑客攻击、传播病毒、严重诈骗等),服务器有权拒绝解封并移交司法机关。

二、解封资格判定

1. 允许申请解封的情况

  • 暂时封禁/禁言/旁观者模式:处罚期限已满,且玩家承认错误并承诺不再重犯。
  • 误判申诉:玩家认为管理团队的处罚决定存在事实错误或证据不足。
  • 情节轻微的永久封禁:除以下“禁止解封”列表外的首次永久封禁,且玩家能提供深刻的书面检讨及补偿方案(如适用)。
  • 特殊情况:弥补自己的过失。

2. 禁止解封(永久拒绝)的情况

根据原条款,以下行为原则上不予解封,除非有确凿证据证明是误判或账号被盗:

  • 严重违法行为:涉及黑客攻击、入侵服务器系统、窃取数据、传播病毒/恶意软件。
  • 严重作弊:使用外挂、修改器、自动化脚本,或利用漏洞刷取物品/经验(特别是多次违规者)。
  • 恶意破坏:故意攻击服务器硬件/网络架构,造成服务器财产损失。
  • 极端言论:散布仇恨、种族/性别/地域歧视,且情节严重者。
  • 盗号行为:试图盗用他人账号、密码或伪造身份。
  • 二次违规:在已经受过一次永久封禁后,再次发现违规(包括多开账号被二次发现)。
  • 非法宣传:第二次在服务器内或社群非法宣传其他MC服务器。
  • 服主特批除外:根据条款第七条,服主拥有最高执行权,若服主特批,可打破上述限制。

三、申诉与解封流程

第一步:提交申诉

玩家需通过服务器官方QQ社群或指定申诉渠道提交书面申诉。申诉必须包含以下内容:

  1. 基本信息:游戏ID、被封禁时间、被封禁原因(若知晓)。
  2. 事实陈述:

    • 若承认违规:详细说明违规经过,分析错误原因,并表达悔过之意。
    • 若认为误判:提供确凿证据(如截图、录像、聊天记录、日志片段等),证明未违反相关条款。
  3. 整改承诺:承诺今后严格遵守《服务器许可条款》,并接受监督。并且需要 $1000$ 字及以上的手写申请书(或者电脑鼠标写字)。
  4. 特殊说明:若涉及“潜规则”(不被发现、举报即可),请注意:主动自首者可酌情减轻处罚,但被他人举报证实者严禁解封。

第二步:管理团队审查

  1. 受理时间:管理团队将在收到申诉后的 0-7 个工作日 内(参照原条款投诉回应时间)进行审查。
  2. 审查内容:

    • 核对违规日志与证据。
    • 评估违规行为的严重程度及对服务器的影响。
    • 核实玩家的历史违规记录。
  3. 多方会诊:对于永久封禁的申诉,需由至少两名管理员共同审核;涉及重大争议时,由服主最终裁定。

第三步:裁决与执行

  1. 维持原判:若证据确凿或违规性质恶劣(属“禁止解封”列表),驳回申诉,维持原处罚。
  2. 减轻处罚:若玩家态度诚恳、初犯且非原则性错误,可将“永久封禁”降级为“长期暂时封禁”(如30天),或缩短现有封禁时间。
  3. 解除处罚:若确认为误判,立即解除封禁/禁言/旁观者模式,并恢复受影响的游戏数据(如有备份)。
  4. 结果通知:审查结果将通过QQ私聊或游戏内邮件通知玩家。

四、特殊情形处理

1. 关于“潜规则”的特别说明

原条款中提到“潜规则:不被发现、举报即可”。

  • 解封政策:若玩家因触发此潜规则被封禁,仅当玩家主动自首并交出作弊工具/方法时,才考虑从轻发落(改为短期封禁)。
  • 严禁解封:若是被其他玩家举报、系统检测发现或被管理员抓现行,一律严格执行原条款处罚,不接受任何以“别人也这么干”为理由的解封申请。

2. 关于未成年人充值退款引发的封禁

原条款规定“增值服务渠道不可以退款,即使是非成年人”。

  • 若因家长强行退款导致账号被封禁,玩家需补齐退款金额及相应手续费后,方可申请解封。若拒绝补齐,账号将永久封禁。

3. 关于招人违规

若因招人时未让新人阅读许可条款而导致招人者受罚:

  • 招人者需重新组织被招入的玩家集体学习条款,并提交学习证明(如截图),方可申请解除对招人者的处罚。

五、权利与免责

  1. 最终解释权:所有解封决定的最终解释权归【繁忙的工艺 FMCRAFT】管理团队所有,服主拥有一票否决权或一票通过权(参照条款第七条)。
  2. 虚假申诉:若在申诉过程中提供伪造证据、恶意辱骂管理人员或煽动舆论,将直接延长封禁时间或升级为永久封禁。
  3. 数据风险:解封后,服务器不保证能完全恢复封禁期间丢失的数据(如因资源过度占用被清理的箱子等),玩家需自行承担部分风险。
  4. 次数限制:每个账号每季度仅限提交 2次 正式解封申诉,频繁恶意申诉将被视为干扰管理秩序。

六、附则

  • 本规则自发布之日起生效,作为《【繁忙的工艺 FMCRAFT】服务器许可条款》的补充文件。
  • 若本规则与原条款有冲突,以原条款及服主的最新公告为准。

V1.0【FMCRAFT 繁忙的工艺】致新玩家.pdf

【FMCRAFT 繁忙的工艺】致新玩家

你好,世界!

欢迎阅读《【FMCRAFT 繁忙的工艺】致新玩家》入服指南。

希望这份指南能帮助到你。


服务器地址

mc.fmcraft.top


请务必阅读 FMCRAFT 我的世界服务器许可条款

当你第一次加入服务器,即代表你同意这些条款。

【FMCRAFT 繁忙的工艺】服务器许可条款 - 壹鲦热带鱼的小窝。


一 我如何加入服务器?

1. 拥有 PCL2 启动器

PCL 最新正式版永久下载地址丨龙腾猫跃丨爱发电:原版。

PCL 社区版:二次开发版,支持深色模式和更多功能。

2. 拥有游戏版本

下载 1.21.4 的原版。

3. 启动游戏 & 添加服务器 & 加入服务器

启动游戏后,单击多人游戏,单击添加服务器,服务器地址填 mc.fmcraft.top,添加即可,然后需要进入服务器。

其实

你可以在互联网上搜索到这些。


二 在游戏中注册一个账户

你需要按下 T 键打开聊天窗口,使用 /reg <Password> <Password> 注册一个属于你自己的账户。

<Password> 即密码。

例如,你想将密码设置为 114514a,指令为 /reg 114514a 114514a。

请务必记住你的密码。找回密码异常困难。


三 一些你可能需要学会的东西

登录游戏账户

使用 /l <Password>。

签到指令

为什么要签到?

可以领取钻石等好东西。

/signin click 直接签到。

/signin gui 打开签到菜单。

进入生存世界

当你第一次进入服务器的时候,你进入的为 minecraft:overworld 即默认世界,但是在 FMCRAFT 中,我们认为这个世界并不是生存世界。

你可以在主城附近找到一个命令方块(即墙壁旁边),按下压力板即可。

如何查看服务器个人积分

长按 Tab,自己玩家名旁边的黄色数字就是。

传送

/tpa <Player> 申请传送到目标玩家。

/tpahere <Player> 申请目标玩家传送到你的位置。

任何传送都需要经过目标玩家的同意。

当你收到一个传送申请时,系统会将此通知以消息的形式通知你,你需要按下 T 键后选择 同意 或 ​拒绝​。

未处理的传送将在 60 秒后过期。

你不可以传送到一个指定的坐标。

箱子锁

使用箱子锁,使你的箱子不会被破坏。

制作一个​橡木告示牌​,然后右键需要保护的箱子。

它同样适用于容器。

领地

使用领地,使你的领地不会被破坏。

为了篇幅简略,请你自行阅读:FMCRAFT Res领地插件使用教程 【官方 and 非官方】 - 壹鲦热带鱼的小窝(15180 字)。

家被炸了,我如何向管理员请求查看修改者

按 F3,获取世界名称和坐标(自己百度搜)。

通过平台私信发送给服务器管理员(建议服主)。

有些教程,这里不会提及

请善用 搜索引擎 和 AI。

只有服务器相关的问题,在服务器官方社群中 @ 管理员才可能被回复。


四 服务器社群

盒子IM 添加 DHEnry 为好友邀请你加入。

Tailchat Nightly 这个链接为邀请链接。

Discord 这个链接为邀请链接。

QQ群 这个链接同样为邀请链接,或可使用 327285832 进入。

更多社群,等你探索...