如何在Linux下架设我的世界(Minecraft)模组(Mod)服务器
多年以后,面对纷繁嘈杂的世界,他仍能回忆起与朋友无虑且放肆地挥霍人生的那个遥远的下午。
前言
“我的世界”这款游戏的影响力,对于笔者这一年龄段的游戏玩家,绝对是毋庸置疑的,就算你不十分了解,也一定听说过它。还记得在笔者读初中的时候第一次接触到它,由于在当时还没有类似的游戏存在,因此这个有着绝对“全开放”世界+生存主题的游戏深深吸引了本人幼小的心灵。在很长一段时间中一度作为本学渣在父母不在时偷偷玩的游戏首选项。
而玩过我的世界的玩家都应该能感受到,这种打怪+搭方块的游戏,单机一个人玩固然有乐趣,然而玩多了总会疲倦,如果有好友一块联机游戏,则体验可以直上一层楼。
但是如何进行联机,则是一个很大的问题,对于普通玩家来说,联机更多地只是在某一方的电脑上打开游戏,在设置中点“开放至局域网”,然后几个人连到同一个WIFI下进入游戏房间。然而这样的方法有很大的局限性:首先便是主机需要一直开着游戏,不能做其他事;然后是所有人必须处于同一个局域网下;这显然是不现实的,毕竟我们总不能天天背着电脑到别人家里联机打游戏。更何况,虽然联机很好,但有些时候也是想一个人玩的,上述方式进行游戏时,必须让存档所在的主机玩家同时打开游戏开服,这显然会给他人带来很多麻烦。
因此,我们需要的实际上是一种服务器的模式:在有朋友需要联机时,大家接入同一个服务器,彼此可见;在所有人下线后,服务器也不会关闭,仍保持运行,这样每个人想要自己玩时也可以随时加入。
而为了实现这样的需求,对于普通玩家就出现了相当大的壁垒:一方面是我们对于服务端/客户端运行模式需要有了解,另一方面,更多的是需要对计算机网络有一定认知。正如笔者所言,当年是初中时接触的这款游戏,然而由于小孩子的智力水平普遍较低(暴论),笔者也不例外,当时本人的智力显然不足以让我学明白开服的原理,再加上当年打游戏更多的只是为了逃避学习,就更没有认真学习如何开服了。
虽说在之后的好多年内,本学渣对着许多教程都没能研究出来如何实现开服,然而这些零零散散的阅读并非毫无作用,在这过程中,笔者不知不觉中也获得了许多“没什么用”的知识:如学习了IP地址后,知道了IPv4公网地址的告罄,也得知了大部分运营商不提供公网IPv4;如了解了NAT/PAT后,就明白了内网下全设备共用同一个公网IP,也就知道了为什么明明家里有公网IP时,对方直接输IP也无法进行远程连接;研究Hamachi时,明白了VPN的大致运行逻辑;在杂七杂八的论坛帖中也了解了一些路由、DDNS、ACL控制、IPv6、Linux系统操作等一系列网络和运维领域的东西。可以说,研究Minecraft的开服,是本人日后各种折腾的知识累积“奇点”。
总而言之吧,在日积月累下,终于在读本科的某一天,本学渣基本了解明白了开服所需的大部分知识体系,于是和室友合租了一台云服务器,开了一个自己整合的mod服,与几位室友在其中快乐地消耗了好一段人生。
然而正所谓:胜地不常,盛筵难再;兰亭已矣,梓泽丘墟。
人生能够肆意挥霍的青春转瞬即逝,本科毕业后,大家都四散忙于各自的人生,再难有当年的心境和时间去娱乐,事实上,时至今日就算再打开Minecraft,我也失去了当年的游玩兴致(可能是所谓的电子阳痿罢),只能朦胧记得曾经似乎在其中得到过快乐,剩下便只有感叹时间的易逝。
虽然客观来说,本人年龄算不上大,因此在这里大发青春感慨总难免是显得惺惺作态、娇柔做作,然而缅怀青春这件事,其实无关年龄。恐怕,只要我们意识到自己离坟墓的距离日益减少,就自然而然会产生出怀念更年轻的自己的感受罢。此文的内容,对于本人大概率已经没有什么用处了,然而我还是要写下这篇文章,以纪念本人在这荒唐的世界中荒唐地度过的一些荒唐的时间。
总览及准备
如标题所述,本文介绍的是将服务器架设在Linux操作系统之上。选择Linux的原因有很多,最重要的原因便是:看着牛逼。尤其是如果你和本人一样,是纯文科专业,没有什么计算机背景,那在Linux上搭完以后,看着一个控制台中不停地刷代码,成就感会很足,周围的人也会感觉你很牛逼。当然了,在懂计算机的朋友面前,最好还是收起来,不要给他们献丑的好。
当然了,Linux也有省资源、启动速度快等优点,但与上述原因相比都不太重要。
那么首先,我们肯定需要有一台安装了Linux系统的计算机,你可以选择在实体机上装Linux,也可以建立一台虚拟机,这里就不介绍方式了,否则篇幅放不下。如果不会的话,可以先学习下基础的Linux系统安装方式。
本人习惯的Linux发行版是Ubuntu,于是本教程基于Ubuntu撰写,如果你想用Debian或者CentOS也可以,但每一步的指令可能会有一些不同,需自行确认。
对于配置方面,开服主要吃的是内存,最好有8g以上的内存,当然,如果你只有两三个一起玩的朋友,那4g也是够用的,2g就比较勉强了,服务器会不定期卡死。CPU方面,总归是越强越好了,最低也得有2GHZ,否则模组服大概率会开不起来。除此之外,网络是重中之重,如果你租云服务器,最好是买不限带宽的流量计费,而不是买带宽计费那种,毕竟国内的带宽实在是太贵了。不过可能不太容易买到就是了。如果你要在自己家开服,那要保证宽带的上行带宽,注意一定要是上行带宽,不是下载带宽,这里最好和宽带运营商确认下,因为现在国内很多千兆网只有下载是千兆,实际只有二十几兆的上行。而开服的话,上行需要大于50兆,最好有100m,当然也是人越多需求越高。
说实话,这里的价格门槛还是有点高的,尤其是对于不富有的学生党,我想大部分是无力一人承担服务器的价格的(反正当年本人是无力承担的),最好还是和朋友合租云服务器或者合购物理机。
安装完系统后,先运行更新:
1 | sudo apt update |
部署原生Java服务端
如果你仅需要搭建一个纯原生的服务器环境,则只需阅读这一部分即可,否则请直接阅读下一章节。
Screen安装
Screen是一个控制台应用,通过Screen建立控制台后,断开服务器的SSH连接不会中断服务运行。
1 | sudo apt install screen |
安装完后,通过下述指令创建一个控制台:
1 | screen -S MC |
重新SSH连入系统后,通过下述指令回到服务端所在窗口:
1 | screen -r MC |
注:新版的Ubuntu自带Screen,所以这步不是必须。
JRE安装
由于Minecraft是基于Java编写,所以在打开服务端前,需要先安装Java运行环境JRE。(JDK也行,会自动包含JRE):
如果你要运行1.17以上的服务端,那么安装最新JRE即可:
1 | sudo apt install default-jre |
如果你要运行1.17以下的服务端,那么需安装Java 8:
1 | sudo apt-get install openjdk-8-jdk-headless |
检查安装的版本:
1 | java -version |
服务端下载
首先创建一个放服务端的文件夹,这里叫做MC
1 | mkdir MC |
进入MC文件夹:
1 | cd MC |
下载服务端文件:
我们这里以本人写文章时的最新服务端为例:
1 | wget https://launcher.mojang.com/v1/objects/c8f83c5655308435b3dcf03c06d9fe8740a77469/server.jar |
为了方便管理,我们对于下载的文件改名:
1 | mv server.jar McServer.jar |
初始化:
1 | java -jar McServer.jar --nogui |
此时会报错,需要先接受EULA,见下文。
接受EULA
初次运行完后,目录下会生成eula.txt
文件,用Vim或其它的文本编辑器将其中的eula=false
改为eula=true
开服
1 | java -Xmx1024M -Xms1024M -jar McServer.jar nogui |
-Xmx -Xms用于指定使用内存的最大最小值(MB),可根据实际使用情况调整。此时我们的最基础的一个服务器就开启完成了。
部署模组服务端(Forge版)
如果你在读这一部分,那么表示你不仅希望玩原生内容,还想打上各类的mod丰富游戏体验。当然,一般来说,总是要打一些Mod的,否则没什么意思。
Screen安装
Screen是一个控制台应用,通过Screen建立控制台后,断开服务器的SSH连接不会中断服务运行。
1 | sudo apt install screen |
安装完后,通过下述指令创建一个控制台:
1 | screen -S MC |
重新SSH连入系统后,通过下述指令回到服务端所在窗口:
1 | screen -r MC |
注:新版的Ubuntu自带Screen,所以这步不是必须。
JRE安装
由于Minecraft是基于Java编写,所以在打开服务端前,需要先安装Java运行环境JRE。(JDK也行,会自动包含JRE):
如果你要运行1.17以上的服务端,那么安装最新JRE即可:
1 | sudo apt install default-jre |
如果你要运行1.17以下的服务端,那么需安装Java 8:
1 | sudo apt-get install openjdk-8-jdk-headless |
检查安装的版本:
1 | java -version |
Forge服务端下载
要安装Mod,则需要装Mod管理器,我们这里使用Forge,你也可以选择Fabric等管理器,这里只展示Forge的用法。
在这里下载对应版本的Forge安装器:
将下载的文件放在服务器目录下,然后:
1 | java -jar forge-x.xx.x-installer.jar --installServer |
等待读条完毕后,进行初始化:
1 | java -jar forge-x.xx.x-universal.jar --nogui |
此时会报错,需要先接受EULA,见下文。
接受EULA
初次运行完后,目录下会生成eula.txt
文件,用Vim或其它的文本编辑器将其中的eula=false
改为eula=true
添加模组(Mods)
这一步很容易,只需在服务器目录下创建一个mods文件夹,然后将想打的mod放进去即可。
这里分享两个mod下载站:
如果你有办法连到外网,那么我推荐使用CurseForge,这里有很多一线的创作者在更新,也可以直接下做好的整合包。
如果你在使用中国大陆的网络,那么我推荐MCBBS,这里既有国内的优秀mod制作者制作的mod,也有搬运国外的优秀mod的贴子。
开服
1 | java -Xmx1024M -Xms512M -jar forge-x.xx.x-universal.jar -nogui |
服务端配置
所有的配置都在文件:server.properties
中
需要留意的是online-mode
这一选项,这是检测连接的客户端是否为正版的选项,我想大部分中文用户都是盗版玩家,所以首先将其改为false,否则会无法连接。
server-port
//这个是占用的端口,默认25565,如果保持默认,那么登陆服务器的时候不用输入端口号
max-players
//这个是最大人数,默认是20人
其余的可以根据自身需求配置,保持默认一般就可以
服务端优化
下述方法可以起到心理层面上的优化作用。
以下是启动脚本
java -server -d64 -Xmx3550M -Xms3550M -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:NewSize=1024m -XX:MaxNewSize=1024m -XX:+UseParNewGC -XX:+CMSIncrementalPacing -XX:+UseFastAccessorMethods -XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=100 -XX:+CMSParallelRemarkEnabled -XX:ParallelGCThreads=20 -jar minecraft_server.1.8.jar nogui
参数说明
-server:一定要作为第一个参数,会使JVM启动速度变慢,但会显着提升JVM性能
-d64:强制使用64位JVM,如果不是64位系统和java请去掉
-Xmx3550m:设置JVM最大堆内存为3550M。
-Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
-Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
-XX:NewSize=1024m:设置年轻代初始值为1024M。
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
-XX:PermSize=256m:设置持久代初始值为256M。
-XX:MaxPermSize=256m:设置持久代最大值为256M。
-XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
GC(垃圾回收)参数
Sun HotSpot JVM 通常使用的 GC 算法是分代回收。即把对象按生命周期不同分为年轻代和年老代。新的对象会先生成在Young Area,在几次 GC 以后,如果没有收集到,就会逐渐升级到Tenured Area。
Sun JVM 默认的回收器称为 serial collector ,即串行回收,我们也可以使用多线程并发回收来提高效率。需要注意的是,在单核的机器上,使用默认的回收器可能会更好。
-XX:+UseParNewGC:为年轻代对象使用并发回收,会缩短年轻代对象的回收时间
-XX:+UseConcMarkSweepGC:为年老代使用并发回收
-XX:+UseParallelGC:该收集器和 UseConcMarkSweepGC 是互相排斥的,为新生代使用并行清除,年老代使用单线程Mark-Sweep-Compact的垃圾收集器。
远程联机设置
开好服务器,当然需要能在公网环境连上才有用,这里探讨几种不同的网络环境下的联机方法:
根据你的网络环境阅读
无V4公网IP,无V6公网IP
(这种情况,首先建议你换家运营商。)
对于IPv4,参考下文中的IPv4内网穿透;
对于IPv6,可能得使用4To6Tunnel之类的技术,不建议折腾,对于这种网络,建议换一家运营商。
有V4公网IP,无V6公网IP
这样的情况可能比较罕见,无法组双栈,但至少IPv4可以稳定连接,参考下文中的端口转发。
参考下文中的IPv4端口转发。
无V4公网IP,有V6公网IP
对于IPv4,参考下文中的IPv4内网穿透。
对于IPv6,参考下文中的IPv6防火墙放行。
有V4公网IP,有V6公网IP
对于IPv4,参考下文中的IPv4端口转发。
对于IPv6,参考下文中的IPv6防火墙放行。
IPv4端口转发
当我们说你有IPv4的公网地址时,实际说的是你的路由器的WAN口有一个公网地址,所有的网络流量都会经由这一个端口在你的内网和公网之间流动,这一切的行为都由路由器控制,因此我们所要设计的是:将哪些网络流量转发至我们的内部服务器?通常来说我们以端口进行过滤。例如MC的服务器默认开启在25565端口,不修改这个的前提下,我们可以在路由器中设置将WAN口所有的25565端口的入站流量,都转发至我们内网服务器的25565端口,这里的操作需要参考你的路由器的说明书,正常来说所有路由器都有这一基础功能。当然这两个端口都可以自定义,但这里就举例最方便的方式了,当我们做好转发后,便可以通过IP:25565的方式在游戏中连接我们的服务器了。
IPv4 内网穿透(不建议)
对于内网穿透,本人并不建议,其本质上就是做了服务器中转,无论是带宽还是延迟都会受到很大影响。
但是对于大多数用户来说,也就只有这一个办法了。(好多年前还可以通过组VPN来实现联机,但现在在国内可能会面临未备案的法律风险,所以这条路可以说已经不通了)
关于内网穿透,免费方案是—FRP
当然,说是免费,你至少也得租一台云服务器才行,这样才能有公网IP。只不过不需要买配置很高的,服务器只要提供带宽即可。
FRP由客户端和服务端组成,服务端的IP就是最终我们用来连接的IP。
然而,与其租台服务器折腾这个,不如直接买现有的付费方案,反正都要花钱。
IPv6 防火墙放行
当你有IPv6时,对于大多数用户来说是无需配置的,直接就可以使用,然而如果你的路由器存在防火墙,则需根据路由器系统,找到IPv6防火墙设置,进行关闭。
当然,关闭防火墙并不是最好的选择,最好的是仅仅将服务器的机器的IP和MC所在端口号进行放行,然而考虑到大部分路由器对于IPv6防火墙的支持很差,不一定有这一功能,在不使用OpenWrt或者其它高级点的路由器系统的情况下,可能无法实现需求,索性就关了就好。
DDNS
最后,当一切都可以运行时,还有一个大幅提升体验性的事情可以做,那就是动态域名服务。由于很多时候,我们就算有公网IP,它也时不时地会变化,这样会导致每次连接时都要先查一下当前IP是什么,很不方便,如果可以像访问谷歌百度一样,每次输入域名,就会容易很多。
为了实现这个,我们首先要购买一个域名,可以买的地方很多,如NameCheap、阿里云等等,这里就不多赘述。
有了我们自己的域名,下一步就是域名与IP绑定,也就是DNS,然而正如前文所述,当IP变化时,DNS就会失效,因此我们需要一个服务动态地将域名与IP进行关联。
要做DDNS,其实工具也有很多,笔者比较熟悉的是ddns-go
,只需在Docker中启动,就可以看到直观的Web管理界面,这里就不赘述细节了,到它的官方文档查看即可。
当这一步做好后,我们就有了一个可以通过域名访问的服务器啦~