1.Paper和Spigot服务器优化基础
1.1 服务器性能指标
- CPU占有率:CPU占有率反映了服务器在处理游戏逻辑、数据计算、网络通信等任务时对CPU资源的使用情况。过高的CPU占有率会导致服务器响应缓慢,影响玩家的游戏体验。例如,当CPU占有率持续超过80%时,可能会出现卡顿、延迟等问题,影响玩家的实时交互和游戏流畅度。
- 带宽使用率:带宽使用率表示服务器在数据传输过程中对网络带宽资源的占用程度。对于Minecraft服务器来说,带宽主要用于玩家数据同步、地图数据传输、插件数据交互等。如果带宽使用率过高,可能会导致网络拥堵,出现丢包、延迟等问题,影响玩家的联机游戏体验。例如,在高并发的玩家数据同步场景下,如果带宽不足,可能会导致玩家看到的游戏画面出现延迟或卡顿。
- 地图读写率:地图读写率是指服务器在加载、保存地图数据时的速度。地图数据是Minecraft游戏的核心内容之一,包括地形、建筑、生物等信息。快速的地图读写能力可以确保玩家在探索新区域、加载存档时获得流畅的体验。例如,当玩家进入一个新的区块时,服务器需要迅速读取该区块的地图数据并发送给玩家,如果读写速度慢,玩家会看到加载进度条停留较长时间,影响游戏的连贯性。
- 网络延迟和丢包率:网络延迟是指数据在网络中传输所需的时间,而丢包率是指数据在传输过程中丢失的比例。对于Minecraft服务器,低延迟和低丢包率是保证玩家实时交互和稳定游戏体验的关键。例如,在PvP战斗中,如果网络延迟较高,玩家的操作可能会出现延迟,导致战斗结果不准确;而丢包率过高则可能导致玩家的游戏画面出现卡顿或断线,严重影响游戏的好评率。
- 实体处理频率:实体处理频率是指服务器对游戏中的实体(如玩家、怪物、物品等)进行更新和处理的频率。合理的实体处理频率可以确保游戏的流畅性和真实性。例如,对于怪物AI的更新,如果处理频率过低,怪物的行为可能会显得犹如在跳青海瑶;而过高的处理频率则会增加服务器的计算负担,可能导致CPU占有率飙升,影响整体性能。
- 内存使用情况(通常是6GB):内存是服务器运行的重要资源,用于存储游戏数据、程序代码等。合理的内存使用可以保证服务器的稳定运行。例如,如果内存使用过高,可能会导致服务器出现卡顿、崩溃等问题(尤其是启动器);而内存使用过低,则可能意味着服务器资源没有得到充分利用,影响性能表现。
2.2 优化目标与策略
CPU的使用率使其保持在一个合理的范围内(CPU的使用率通常在10%到30%之间,使用了光影模组或大量复杂模组,CPU使用率可能会更高,达到50%甚至更高,tip:使用独立显卡,非独立显卡最好不要加光影,也可以将java.exe设为高性能,或使用mod优化,如果实在拿不出来高配置就降低渲染距离和关闭高级图形效果!),以提高服务器的响应速度和处理能力。策略包括优化游戏逻辑代码,减少不必要的计算;合理配置服务器参数,如调整tick速率、优化插件加载顺序等;使用多线程技术,将一些耗时的任务分配到不同的线程中执行,提高CPU的利用率。
降低带宽的占用,提高网络传输的效率。策略包括优化数据传输协议,减少数据包的大小和传输次数;使用数据压缩技术,对传输的数据进行压缩处理,降低带宽消耗;合理配置网络设备,如路由器、交换机等,优化网络流量的分配和管理、采用更先进的网络协议,如QUIC协议,优化网络设备的配置,如调整路由器的QoS设置,优先处理游戏数据包;使用网络加速技术,如CDN加速、数据包优化等,提高数据传输的速度和可靠性。
根据服务器的实际情况和网络环境,自动选择最优的端口配置,以提高网络通信的效率和稳定性。策略包括开发智能端口选择算法,根据网络流量、延迟、丢包等指标,动态调整端口配置;结合机器学习技术,对历史数据进行分析和学习,预测最优的端口配置方案。
写了个主程序的代码(只因不想写了,大佬请自补)(排版可能有些问题)
import os
import subprocess
import time
import json
# 初始化配置
network_threshold = {"latency": 50, "packet_loss": 1}
port_score_standard = {"throughput_weight": 0.4, "stability_weight": 0.3, "security_weight": 0.3}
monitor_interval = 60 # 每分钟监测一次
# 网络环境监测函数
def monitor_network():
latency = subprocess.run(["ping", "-c", "4", "major_player_region_ip"], capture_output=True, text=True)
packet_loss = subprocess.run(["some_packet_loss_detection_command"], capture_output=True, text=True)
# 解析命令输出,提取延迟和丢包率数据
latency = float(latency.stdout.split("avg = ")[1].split("ms")[0])
packet_loss = float(packet_loss.stdout.split("% packet loss")[0])
return {"latency": latency, "packet_loss": packet_loss}
# 端口性能分析函数
def analyze_ports():
ports_data = {}
for port in range(25565, 25575): # 假设检测25565到25574端口
throughput = subprocess.run(["some_throughput_detection_command", str(port)], capture_output=True, text=True)
stability = subprocess.run(["some_stability_detection_command", str(port)], capture_output=True, text=True)
# 计算端口得分
score = (float(throughput.stdout) * port_score_standard["throughput_weight"] +
float(stability.stdout) * port_score_standard["stability_weight"] +
some_security_score_function(port) * port_score_standard["security_weight"])
ports_data[port] = {"score": score, "throughput": throughput, "stability": stability}
return ports_data
# 最优端口推荐函数
def recommend_optimal_port(ports_data):
optimal_port = max(ports_data, key=lambda x: ports_data[x]["score"])
return optimal_port
# 自动配置端口函数
def configure_minecraft_port(port):
subprocess.run(["some_minecraft_server_config_command", str(port)])
subprocess.run(["systemctl", "restart", "minecraft_service"])
# 主循环
while True:
network_data = monitor_network()
if network_data["latency"] > network_threshold["latency"] or network_data["packet_loss"] > network_threshold["packet_loss"]:
ports_data = analyze_ports()
optimal_port = recommend_optimal_port(ports_data)
configure_minecraft_port(optimal_port)
# 监测配置效果并收集反馈
time.sleep(30) # 等待30秒让配置生效
feedback = subprocess.run(["some_feedback_collection_command"], capture_output=True, text=True)
if "negative" in feedback.stdout:
# 若反馈负面,重新分析端口
continue
time.sleep(monitor_interval)
2. 性能比较
2.1 TPS和延迟优化
Paper和Spigot都是基于Bukkit API的Minecraft服务器软件,但Paper在性能优化方面更为突出。根据测试,Paper在处理高并发玩家和复杂游戏逻辑时,能够提供更高的TPS(每秒事务数)和更低的延迟。
- TPS提升:Paper通过优化服务器的内部机制,如改进区块加载和卸载的效率、优化实体和玩家的更新逻辑等,使得服务器能够更高效地处理游戏内的各种事件。在高负载情况下,Paper的TPS比Spigot高出约20%至30%。
- 延迟降低:Paper引入了异步处理机制,能够将一些耗时的操作(如数据库操作、网络请求等)放到单独的线程中执行,从而减少对主线程的阻塞。此外,Paper还优化了网络数据包的处理流程,降低了数据传输的延迟。在实际测试中,使用Paper的服务器平均延迟比Spigot低约15%至25%。
3.配置文件优化
3.1 server.properties设置
在Minecraft服务器的配置文件中, server.properties 是基础且关键的配置文件,它包含了服务器运行的基本参数设置。对于Paper和Spigot来说,虽然它们都基于这个文件,但Paper在某些配置项上提供了更优化的默认设置和额外的配置选项。
- 模拟距离( simulationdistance ):Paper和Spigot都允许调整模拟距离,这个参数决定了服务器在多大范围内对区块进行模拟(如作物生长、动物行为等)。Paper默认将模拟距离设置得较低,通常在4到6之间,这有助于减少服务器的计算负担,提高性能。而Spigot的默认值通常较高,可能在8到10之间,这会导致更多的区块被模拟,从而增加服务器的负载。
- 视距( view-distance ):视距决定了玩家能看到多远的区块。Paper建议将视距设置在7到10之间,以平衡玩家的视觉体验和服务器性能。Spigot的默认视距通常较高,可能导致服务器在加载区块时出现性能瓶颈。
- 网络压缩阈值( networkcompression-threshold ):Paper和Spigot都允许设置网络数据包的压缩阈值。Paper推荐将此值设置为256,这有助于在保证网络性能的同时,减少带宽的使用。Spigot的默认设置可能更高,这在某些情况下可能会导致网络延迟增加。
3.2 bukkit.yml和spigot.yml配置
bukkit.yml 和 spigot.yml 文件包含了更详细的服务器配置选项,这些选项可以进一步优化服务器的性能和行为。
- 生物生成限制( spawn-limits ):在 bukkit.yml 中,Paper和Spigot都允许设置生物的生成限制。Paper通常会将怪物的生成限制设置得较低,例如20个怪物,这有助于减少服务器的负载,同时保持游戏的平衡。Spigot的默认设置可能更高,这可能导致服务器在处理大量生物时出现性能问题。
- 实体激活范围( entity-activation-range ):在 spigot.yml 中,Paper提供了更精细的实体激活范围设置。通过减少激活范围,Paper能够减少服务器对实体的更新频率,从而提高性能。例如,Paper可能会将怪物的激活范围设置为24,而Spigot的默认设置可能更高。
- 漏斗操作频率( hoppertransfer 和 hopper-check ):Paper和Spigot都允许调整漏斗的操作频率。Paper推荐将漏斗的物品转移频率和检查频率设置为8,这有助于减少漏斗对服务器性能的影响,同时保证漏斗的正常工作。
3.3 paper.yml高级设置
paper.yml 是Paper特有的配置文件,它提供了一些高级设置选项,这些选项可以进一步优化服务器的性能。
- 区块卸载延迟( delay-chunk-unloads-by ):Paper允许设置区块卸载的延迟时间。通过增加延迟时间,可以减少区块频繁加载和卸载对服务器性能的影响。例如,将延迟时间设置为10秒,可以有效地减少区块加载的频率。
- 防止进入未加载区块( prevent-moving-into-unloaded-chunks ):Paper提供了防止玩家进入未加载区块的设置。启用这个选项可以避免玩家在移动时遇到未加载区块导致的卡顿问题,从而提高玩家的游戏体验。
- 异步怪物生成( enable-async-mob-spawning ):Paper支持异步怪物生成,这可以将怪物生成的计算任务分配到多个线程中执行,从而减少对主线程的阻塞,提高服务器的性能。
Paper在性能和资源使用方面相较于Spigot具有明显的优势。这些优化不仅提高了服务器的TPS和降低了延迟,还减少了内存和资源的消耗,使得Paper成为Minecraft服务器优化的首选。
3.4自定义优化代码示例
- 自定义的区块加载优化代码:
public class CustomChunkLoader implements ChunkLoader {
private final World world;
private final int centerX;
private final int centerZ;
public CustomChunkLoader(World world, int centerX, int centerZ) {
this.world = world;
this.centerX = centerX;
this.centerZ = centerZ;
}
@Override
public Collection<Chunk> getLoadedChunks() {
List<Chunk> loadedChunks = new ArrayList<>();
for (int x = centerX - 5; x <= centerX + 5; x++) {
for (int z = centerZ - 5; z <= centerZ + 5; z++) {
Chunk chunk = world.getChunkAt(x, z);
if (chunk != null && !chunk.isLoaded()) {
world.loadChunk(x, z);
}
loadedChunks.add(chunk);
}
}
return loadedChunks;
}
@Override
public void unloadChunks() {
for (int x = centerX - 5; x <= centerX + 5; x++) {
for (int z = centerZ - 5; z <= centerZ + 5; z++) {
Chunk chunk = world.getChunkAt(x, z);
if (chunk != null && chunk.isLoaded()) {
world.unloadChunk(chunk);
}
}
}
}
}
这个自定义的区块加载优化代码,创建了一个 CustomChunkLoader 类,实现了 ChunkLoader 接口。它可以根据指定的中心坐标,加载和卸载周围一定范围内的区块。通过这种方式,可以实现对特定区域的区块进行优化加载和卸载,提高服务器的性能。
- 自定义的实体更新优化代码:
public class CustomEntityUpdater implements EntityUpdater {
private final World world;
public CustomEntityUpdater(World world) {
this.world = world;
}
@Override
public void updateEntities() {
for (Entity entity : world.getEntities()) {
if (entity instanceof Mob) {
Mob mob = (Mob) entity;
if (mob.getTarget() == null || mob.getTarget().isDead()) {
mob.setTarget(null);
}
if (mob.isOnGround()) {
mob.setVelocity(new Vector(0, 0, 0));
}
}
}
}
}
这个自定义的实体更新优化代码,创建了一个 CustomEntityUpdater 类,实现了 EntityUpdater 接口。它在更新实体时,对怪物实体进行了特殊处理。例如,如果怪物的目标不存在或已死亡,则清除怪物的目标;如果怪物在地面上,则将其速度设置为零。通过这种方式,可以减少不必要的实体更新操作,提高服务器的性能。
4 .实战应用建议
- 选择合适的服务器软件:对于追求高性能和稳定性的Minecraft服务器,Paper是更优的选择,尤其是在高负载和大规模地图的情况下。
- 合理配置服务器参数:根据服务器的具体需求和硬件配置,合理调整 server.properties 、 bukkit.yml 、 spigot.yml 和 paper.yml 等配置文件中的参数,以达到最佳的性能表现。
最后视大家元旦快乐!(◦˙▽˙◦)🎉🎉🎉
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新年快乐烟花效果</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: black;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="fireworksCanvas"></canvas>
<script>
const canvas = document.getElementById('fireworksCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const fireworks = [];
const particles = [];
function random(min, max) {
return Math.random() * (max - min) + min;
}
function Firework(sx, sy, tx, ty) {
this.x = sx;
this.y = sy;
this.sx = sx;
this.sy = sy;
this.tx = tx;
this.ty = ty;
this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
this.distanceTraveled = 0;
this.coordinates = [];
this.coordinateCount = 3;
while (this.coordinateCount--) {
this.coordinates.push([this.x, this.y]);
}
this.angle = Math.atan2(ty - sy, tx - sx);
this.speed = 2;
this.acceleration = 1.05;
this.brightness = random(50, 70);
this.targetRadius = 1;
}
function Particle(x, y) {
this.x = x;
this.y = y;
this.coordinates = [];
this.coordinateCount = 5;
while (this.coordinateCount--) {
this.coordinates.push([this.x, this.y]);
}
this.angle = random(0, Math.PI * 2);
this.speed = random(1, 10);
this.friction = 0.95;
this.gravity = 1;
this.hue = random(0, 360);
this.brightness = random(50, 80);
this.alpha = 1;
this.decay = random(0.015, 0.03);
}
function calculateDistance(p1x, p1y, p2x, p2y) {
let xDistance = p1x - p2x;
let yDistance = p1y - p2y;
return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
}
function launchFirework(sx, sy, tx, ty) {
const firework = new Firework(sx, sy, tx, ty);
fireworks.push(firework);
}
function drawFireworks() {
for (let i = fireworks.length - 1; i >= 0; i--) {
const firework = fireworks[i];
firework.coordinates.push([firework.x, firework.y]);
if (firework.coordinates.length > firework.coordinateCount) {
firework.coordinates.shift();
}
ctx.beginPath();
ctx.moveTo(firework.coordinates[0][0], firework.coordinates[0][1]);
for (let j = 1; j < firework.coordinates.length; j++) {
ctx.lineTo(firework.coordinates[j][0], firework.coordinates[j][1]);
}
ctx.strokeStyle = 'hsl(' + firework.brightness + ', 100%, 50%)';
ctx.stroke();
firework.distanceTraveled = calculateDistance(firework.sx, firework.sy, firework.x, firework.y);
firework.speed *= firework.acceleration;
const vx = Math.cos(firework.angle) * firework.speed;
const vy = Math.sin(firework.angle) * firework.speed;
firework.distanceToTarget -= firework.speed;
firework.x += vx;
firework.y += vy;
if (firework.distanceTraveled >= firework.distanceToTarget) {
for (let j = 0; j < 30; j++) {
const particle = new Particle(firework.tx, firework.ty);
particles.push(particle);
}
fireworks.splice(i, 1);
}
}
}
function drawParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.coordinates.push([particle.x, particle.y]);
if (particle.coordinates.length > particle.coordinateCount) {
particle.coordinates.shift();
}
ctx.beginPath();
ctx.moveTo(particle.coordinates[0][0], particle.coordinates[0][1]);
for (let j = 1; j < particle.coordinates.length; j++) {
ctx.lineTo(particle.coordinates[j][0], particle.coordinates[j][1]);
}
ctx.strokeStyle = 'hsl(' + particle.hue + ', 100%, ' + particle.brightness + '%)';
ctx.stroke();
particle.x += Math.cos(particle.angle) * particle.speed;
particle.y += Math.sin(particle.angle) * particle.speed;