Skip to content

Commit 50ef43f

Browse files
committed
add worldedit support
1 parent b26a1af commit 50ef43f

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed

build.gradle

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ repositories {
4949
name = 'luckperms'
5050
url = 'https://repo.luckperms.net/'
5151
}
52+
53+
// EngineHub (WorldEdit)
54+
maven {
55+
url = 'https://maven.enginehub.org/repo/'
56+
}
5257
}
5358

5459
dependencies {
@@ -92,7 +97,10 @@ dependencies {
9297
compileOnly("me.clip:placeholderapi:2.11.7")
9398

9499
// LuckPerms API
95-
compileOnly("net.luckperms:api:5.4")
100+
compileOnly("net.luckperms:api:5.4")
101+
102+
// WorldEdit
103+
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0")
96104
}
97105

98106
tasks.withType(JavaCompile).configureEach {
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package me.mapacheee.extendedhorizons.integration.worldedit;
2+
3+
import com.google.inject.Inject;
4+
import com.sk89q.worldedit.WorldEdit;
5+
import com.sk89q.worldedit.bukkit.BukkitAdapter;
6+
import com.sk89q.worldedit.event.extent.EditSessionEvent;
7+
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
8+
import com.sk89q.worldedit.extent.Extent;
9+
import com.sk89q.worldedit.math.BlockVector3;
10+
import com.sk89q.worldedit.world.block.BlockStateHolder;
11+
import com.sk89q.worldedit.WorldEditException;
12+
import com.sk89q.worldedit.util.eventbus.Subscribe;
13+
import com.thewinterframework.service.annotation.Service;
14+
import com.thewinterframework.service.annotation.lifecycle.OnDisable;
15+
import com.thewinterframework.service.annotation.lifecycle.OnEnable;
16+
import me.mapacheee.extendedhorizons.ExtendedHorizonsPlugin;
17+
import me.mapacheee.extendedhorizons.shared.service.ConfigService;
18+
import me.mapacheee.extendedhorizons.shared.utils.ChunkUtils;
19+
import me.mapacheee.extendedhorizons.viewdistance.service.FakeChunkService;
20+
import org.bukkit.Bukkit;
21+
import org.bukkit.World;
22+
23+
import java.util.HashSet;
24+
import java.util.Map;
25+
import java.util.Set;
26+
import java.util.UUID;
27+
import java.util.concurrent.ConcurrentHashMap;
28+
import java.util.concurrent.TimeUnit;
29+
30+
@Service
31+
public class WorldEditService {
32+
33+
private final FakeChunkService fakeChunkService;
34+
private final ExtendedHorizonsPlugin plugin;
35+
private boolean enabled = false;
36+
37+
private final Map<UUID, Set<Long>> modifiedChunks = new ConcurrentHashMap<>();
38+
39+
@Inject
40+
public WorldEditService(FakeChunkService fakeChunkService, ExtendedHorizonsPlugin plugin) {
41+
this.fakeChunkService = fakeChunkService;
42+
this.plugin = plugin;
43+
}
44+
45+
@OnEnable
46+
public void onEnable() {
47+
if (Bukkit.getPluginManager().isPluginEnabled("WorldEdit")) {
48+
try {
49+
WorldEdit.getInstance().getEventBus().register(this);
50+
enabled = true;
51+
startFlushTask();
52+
} catch (Throwable t) {
53+
plugin.getLogger().warning("[EH] Failed to register WorldEdit listener: " + t.getMessage());
54+
}
55+
}
56+
}
57+
58+
@OnDisable
59+
public void onDisable() {
60+
if (enabled) {
61+
try {
62+
WorldEdit.getInstance().getEventBus().unregister(this);
63+
} catch (Throwable ignored) {
64+
}
65+
}
66+
}
67+
68+
@Subscribe
69+
public void onEditSession(EditSessionEvent event) {
70+
if (!enabled || event.getWorld() == null)
71+
return;
72+
73+
World world = BukkitAdapter.adapt(event.getWorld());
74+
if (world == null)
75+
return;
76+
77+
event.setExtent(new ChunkTrackingExtent(event.getExtent(), world.getUID()));
78+
}
79+
80+
private void startFlushTask() {
81+
Bukkit.getAsyncScheduler().runAtFixedRate(plugin, (task) -> {
82+
if (modifiedChunks.isEmpty())
83+
return;
84+
85+
for (UUID worldId : new HashSet<>(modifiedChunks.keySet())) {
86+
Set<Long> keys = modifiedChunks.remove(worldId);
87+
if (keys != null && !keys.isEmpty()) {
88+
World world = Bukkit.getWorld(worldId);
89+
if (world != null) {
90+
fakeChunkService.refreshChunks(world, keys);
91+
}
92+
}
93+
}
94+
}, 500L, 500L, TimeUnit.MILLISECONDS);
95+
}
96+
97+
private class ChunkTrackingExtent extends AbstractDelegateExtent {
98+
private final UUID worldId;
99+
100+
public ChunkTrackingExtent(Extent extent, UUID worldId) {
101+
super(extent);
102+
this.worldId = worldId;
103+
}
104+
105+
@Override
106+
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position,
107+
B block) throws WorldEditException {
108+
boolean result = super.setBlock(position, block);
109+
if (result) {
110+
recordChange(position);
111+
}
112+
return result;
113+
}
114+
115+
private void recordChange(BlockVector3 pos) {
116+
int chunkX = pos.getBlockX() >> 4;
117+
int chunkZ = pos.getBlockZ() >> 4;
118+
long key = ChunkUtils.packChunkKey(chunkX, chunkZ);
119+
120+
modifiedChunks.computeIfAbsent(worldId, k -> ConcurrentHashMap.newKeySet()).add(key);
121+
}
122+
}
123+
}

src/main/java/me/mapacheee/extendedhorizons/viewdistance/service/FakeChunkService.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,42 @@ public boolean isFakeChunk(UUID playerId, long chunkKey) {
417417
.orElse(false);
418418
}
419419

420+
public void refreshChunks(World world, Set<Long> chunkKeys) {
421+
if (chunkKeys == null || chunkKeys.isEmpty() || world == null) {
422+
return;
423+
}
424+
425+
for (Long key : chunkKeys) {
426+
chunkLoaderService.invalidateChunk(key);
427+
}
428+
429+
double borderCenterX = world.getWorldBorder().getCenter().getX();
430+
double borderCenterZ = world.getWorldBorder().getCenter().getZ();
431+
double borderSize = world.getWorldBorder().getSize();
432+
433+
for (UUID playerId : playerStateManager.getAllPlayerIds()) {
434+
Player player = Bukkit.getPlayer(playerId);
435+
if (player == null || !player.isOnline() || !player.getWorld().getUID().equals(world.getUID())) {
436+
continue;
437+
}
438+
439+
PlayerChunkState state = playerStateManager.getOrCreate(playerId);
440+
Set<Long> fakeChunks = state.getFakeChunks();
441+
442+
Set<Long> toRefresh = new HashSet<>();
443+
for (Long key : chunkKeys) {
444+
if (fakeChunks.contains(key)) {
445+
fakeChunks.remove(key);
446+
toRefresh.add(key);
447+
}
448+
}
449+
450+
if (!toRefresh.isEmpty()) {
451+
sendFakeChunks(player, toRefresh, borderCenterX, borderCenterZ, borderSize);
452+
}
453+
}
454+
}
455+
420456
private boolean isWithinServerDistance(Player player, int chunkX, int chunkZ) {
421457
int viewDistance = player.getViewDistance();
422458
if (viewDistance <= 0) {

0 commit comments

Comments
 (0)