/
NoLaggChunks.java
196 lines (174 loc) · 8.19 KB
/
NoLaggChunks.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package com.bergerkiller.bukkit.nolagg.chunks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import net.minecraft.server.ChunkCoordinates;
import net.minecraft.server.WorldServer;
import org.bukkit.Chunk;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import com.bergerkiller.bukkit.common.MessageBuilder;
import com.bergerkiller.bukkit.common.Task;
import com.bergerkiller.bukkit.common.config.ConfigurationNode;
import com.bergerkiller.bukkit.common.permissions.NoPermissionException;
import com.bergerkiller.bukkit.common.utils.CommonUtil;
import com.bergerkiller.bukkit.common.utils.MathUtil;
import com.bergerkiller.bukkit.common.utils.StringUtil;
import com.bergerkiller.bukkit.common.utils.WorldUtil;
import com.bergerkiller.bukkit.nolagg.NoLagg;
import com.bergerkiller.bukkit.nolagg.NoLaggComponent;
import com.bergerkiller.bukkit.nolagg.Permission;
import com.bergerkiller.bukkit.nolagg.chunks.antiloader.DummyInstanceMap;
/*
* Important note:
* The classes in the antiloader package ensure that chunks are NOT loaded by the Player Instance objects
* Instead the sending queue is used to load the chunks. If any of the many component fail to initialize, this is not active.
*/
public class NoLaggChunks extends NoLaggComponent {
private static final int UNLOAD_INTERVAL = 200; // Tick interval between chunk unload tries
private static Task chunkUnloadTask;
public static NoLaggChunks plugin;
public static boolean isOreObfEnabled = false;
public static boolean useBufferedLoading = true;
public static boolean useDynamicView = true;
public static boolean hasDynamicView = false;
@Override
public void updateDependency(Plugin plugin, String pluginName, boolean enabled) {
if (pluginName.equals("Chunk Manager")) {
if (enabled) {
log(Level.WARNING, "Chunk Manager detected, NoLaggChunks has been disabled.");
log(Level.WARNING, "Either disable Chunk Manager or disable NoLaggChunks.");
this.disable();
}
} else if (pluginName.equals("Orebfuscator")) {
if (isOreObfEnabled = enabled && useBufferedLoading) {
log(Level.INFO, "Orebfuscation has been detected and will be used when sending chunks");
log(Level.INFO, "Note that this may require you to set more threads used for sending!");
}
}
}
@Override
public void onReload(ConfigurationNode config) {
config.setHeader("minRate", "The minimum chunk sending rate (chunks/tick)");
config.setHeader("maxRate", "The maximum chunk sending rate (chunks/tick)");
config.setHeader("bufferedLoader", "If you use a plugin that depends on the net server handler for packets, disable this");
config.addHeader("bufferedLoader", "For example, Raw Critics' Ore Obfuscation does not function with this enabled.");
config.addHeader("bufferedLoader", "Orebfuscator is supported and works with this enabled.");
config.setHeader("bufferedLoader.enabled", "Whether or not to use the buffered packet loader to reduce new memory allocation");
config.setHeader("bufferedLoader.threadCount", "The amount of threads to use to compress the chunk packets (increase if it can't keep up)");
config.setHeader("useDynamicView", "Sets whether the dynamic view distance should be enforced");
config.addHeader("useDynamicView", "If you use maxTPS, set this to false, or it will conflict!");
config.setHeader("dynamicView", "Sets multiple view distances for different amounts of loaded chunks (chunk_count: view_chunks)");
config.addHeader("dynamicView", "To disable, remove all chunk: view nodes. The view is smoothed out between nodes");
config.addHeader("dynamicView", "The dynamic view distance will never be higher than the server view distance!");
config.setHeader("sendOrder", "Sets in what order chunks are sent to the client");
config.addHeader("sendOrder", "Available modes: " + StringUtil.combineNames(Arrays.asList(ChunkSendMode.values())));
ChunkSendQueue.minRate = config.get("minRate", 0.25);
ChunkSendQueue.maxRate = config.get("maxRate", 1.50);
useBufferedLoading = config.get("bufferedLoader.enabled", true);
useDynamicView = config.get("useDynamicView", true);
ChunkCompressionThread.init(config.get("bufferedLoader.threadCount", 2));
if (!config.contains("dynamicView")) {
// Generate default views
config.set("dynamicView", Arrays.asList("0 = 13", "5000 = 13", "10000 = 13", "60000 = 13"));
}
ChunkCoordComparator.init(config.get("sendOrder", ChunkSendMode.SLOPE));
DynamicViewDistance.init(config.getList("dynamicView", String.class));
}
public void onEnable(ConfigurationNode config) {
plugin = this;
this.register(NLCListener.class);
this.onReload(config);
ChunkSendQueue.init();
DummyInstanceMap.ENABLED = true;
// Start chunk unloading task
chunkUnloadTask = new ChunkUnloadTask(NoLagg.plugin).start(UNLOAD_INTERVAL, UNLOAD_INTERVAL);
}
private static class ChunkUnloadTask extends Task {
public ChunkUnloadTask(JavaPlugin plugin) {
super(plugin);
}
public void run() {
for (WorldServer world : WorldUtil.getWorlds()) {
ArrayList<Chunk> unloadChunks = new ArrayList<Chunk>();
for (net.minecraft.server.Chunk nmschunk : WorldUtil.getChunks(world)) {
Chunk chunk = nmschunk.bukkitChunk;
// Part of world spawn?
if (nmschunk.world.keepSpawnInMemory) {
ChunkCoordinates chunkcoordinates = nmschunk.world.getSpawn();
int centerSpawnX = nmschunk.x * 16 + 8 - chunkcoordinates.x;
int centerSpawnZ = nmschunk.z * 16 + 8 - chunkcoordinates.z;
final int short1 = 128;
if (centerSpawnX >= -short1 && centerSpawnX <= short1 && centerSpawnZ >= -short1 && centerSpawnZ <= short1) {
continue;
}
}
if (!chunk.getWorld().isChunkInUse(chunk.getX(), chunk.getZ())) {
// Event
if (!CommonUtil.callEvent(new ChunkUnloadEvent(chunk)).isCancelled()) {
unloadChunks.add(chunk);
}
}
}
for (Chunk chunk : unloadChunks) {
chunk.unload();
}
}
}
};
public void onDisable(ConfigurationNode config) {
ChunkSendQueue.deinit();
ChunkCompressionThread.deinit();
DynamicViewDistance.deinit();
DummyInstanceMap.ENABLED = false;
Task.stop(chunkUnloadTask);
chunkUnloadTask = null;
}
@Override
public boolean onCommand(CommandSender sender, String[] args) throws NoPermissionException {
if (args.length == 0)
return false;
if (args[0].equalsIgnoreCase("sending")) {
double avgrate = MathUtil.round(ChunkSendQueue.getAverageRate(), 2);
double compbus = MathUtil.round(ChunkSendQueue.compressBusyPercentage, 2);
if (sender instanceof Player) {
Permission.CHUNKS_SENDING.handle(sender);
// show sending information of the player
ChunkSendQueue queue = ChunkSendQueue.bind((Player) sender);
if (queue != null) {
MessageBuilder msg = new MessageBuilder();
msg.green("You receive ").yellow(MathUtil.round(queue.getRate(), 2));
msg.green(" chunks each tick (");
msg.yellow(avgrate).yellow(" avg").green(")").newLine();
msg.green("You have received ");
int sent = CommonUtil.chunkArea - queue.getPendingSize();
double per = MathUtil.round((double) sent / (double) CommonUtil.chunkArea * 100.0, 2);
msg.yellow(sent).white("/").yellow(CommonUtil.chunkArea).green(" chunks (");
msg.yellow(per, "%").green(")").newLine();
msg.green("Your packet buffer is ").green(queue.getBufferLoadMsg()).green(" used");
msg.newLine().green("Chunk compression is ");
if (compbus > 70) {
msg.red(compbus, "%");
} else if (compbus > 40) {
msg.yellow(compbus, "%");
} else {
msg.green(compbus, "%");
}
msg.green(" of the time busy");
msg.send(sender);
} else {
sender.sendMessage("An unknown error occured!");
}
} else {
// show average sending info
sender.sendMessage("Average chunk sending rate: " + avgrate + " chunks each tick");
sender.sendMessage("Chunk compression is " + compbus + "% of the time busy");
}
return true;
}
return false;
}
}