Skip to content

Commit

Permalink
Merge pull request #4 from TWME-TW/clean-code-0103
Browse files Browse the repository at this point in the history
Fixed an issue when transferring a large number of chunks
  • Loading branch information
TWME-TW authored Jan 3, 2025
2 parents badfb90 + 820c389 commit 16568fa
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 110 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>dev.twme</groupId>
<artifactId>WorldEditSync</artifactId>
<version>0.0.2</version>
<version>0.0.3</version>
<packaging>jar</packaging>

<name>WorldEditSync</name>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/dev/twme/worldeditsync/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ public class Constants {
public static final String CHANNEL = "worldedit-sync:main";
public static final int DEFAULT_CHUNK_SIZE = 512; // 32KB
public static final long SESSION_TIMEOUT = 30000; // 30 seconds
public static final int MAX_CHUNKS = 1024; // 最大區塊數
public static final int MAX_CHUNKS = 16384; // 最大區塊數
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public class TransferSession {
private final int chunkSize;
private final Map<Integer, byte[]> chunks;
private long lastUpdateTime;
private TransferStatus status;

public TransferSession(UUID playerUuid, String sessionId, int totalChunks, int chunkSize) {
this.playerUuid = playerUuid;
Expand All @@ -20,7 +19,6 @@ public TransferSession(UUID playerUuid, String sessionId, int totalChunks, int c
this.chunkSize = chunkSize;
this.chunks = new ConcurrentHashMap<>();
this.lastUpdateTime = System.currentTimeMillis();
this.status = TransferStatus.PENDING;
}

public void addChunk(int index, byte[] data) {
Expand Down Expand Up @@ -68,15 +66,11 @@ public int getChunkSize() {
return chunkSize;
}

public long getLastUpdateTime() {
return lastUpdateTime;
}

public TransferStatus getStatus() {
return status;
public int getChunkCount() {
return chunks.size();
}

public void setStatus(TransferStatus status) {
this.status = status;
public long getLastUpdateTime() {
return lastUpdateTime;
}
}
34 changes: 34 additions & 0 deletions src/main/java/dev/twme/worldeditsync/paper/UpdateChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.twme.worldeditsync.paper;

import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Scanner;
import java.util.function.Consumer;

public class UpdateChecker {

private final JavaPlugin plugin;
private final int resourceId;

public UpdateChecker(JavaPlugin plugin, int resourceId) {
this.plugin = plugin;
this.resourceId = resourceId;
}

public void getVersion(final Consumer<String> consumer) {
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
URI uri = URI.create("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId);
try (InputStream is = uri.toURL().openStream(); Scanner scann = new Scanner(is)) {
if (scann.hasNext()) {
consumer.accept(scann.next());
}
} catch (IOException e) {
plugin.getLogger().info("Unable to check for updates: " + e.getMessage());
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public class WorldEditSyncPaper extends JavaPlugin {

@Override
public void onEnable() {

new UpdateChecker(this, 121682).getVersion(version -> {
if (this.getPluginMeta().getVersion().equals(version)) {
getLogger().info("There is not a new update available.");
} else {
getLogger().info("There is a new update available.");
}
});
// 初始化組件
this.clipboardManager = new ClipboardManager(this);
this.worldEditHelper = new WorldEditHelper(this);
Expand All @@ -30,7 +38,7 @@ public void onEnable() {
// getLogger().info("註冊通道: " + Constants.CHANNEL);

// 啟動剪貼簿監視器
clipboardWatcher.runTaskTimerAsynchronously(this, 20L, 20L);
clipboardWatcher.runTaskTimerAsynchronously(this, 40L, 20L);

// 註冊監聽器
getServer().getPluginManager().registerEvents(playerListener, this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
package dev.twme.worldeditsync.paper.clipboard;

import com.google.common.hash.Hashing;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import dev.twme.worldeditsync.common.Constants;
import dev.twme.worldeditsync.common.transfer.TransferSession;
import dev.twme.worldeditsync.paper.WorldEditSyncPaper;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class ClipboardManager {
private final WorldEditSyncPaper plugin;
Expand Down Expand Up @@ -64,48 +55,7 @@ public byte[] getLocalData(UUID playerUuid) {
public String calculateClipboardHash(Clipboard clipboard) {
if (clipboard == null) return "";

try {
StringBuilder contentBuilder = new StringBuilder();

// 加入區域大小信息
com.sk89q.worldedit.regions.Region region = clipboard.getRegion();
contentBuilder.append(region.getWidth())
.append(":")
.append(region.getHeight())
.append(":")
.append(region.getLength())
.append(":");

// 加入原點信息
BlockVector3 origin = clipboard.getOrigin();
contentBuilder.append(origin.x())
.append(":")
.append(origin.y())
.append(":")
.append(origin.z())
.append(":");

// 加入方塊數據
for (BlockVector3 pt : region) {
BaseBlock block = clipboard.getFullBlock(pt);
contentBuilder.append(block.getAsString());
}

for (Entity entity: clipboard.getEntities()) {
contentBuilder.append(entity.toString());
}



// 計算雜湊值
return Hashing.sha256()
.hashString(contentBuilder.toString(), StandardCharsets.UTF_8)
.toString();

} catch (Exception e) {
plugin.getLogger().warning("An error occurred while calculating clipboard hash: " + e.getMessage());
return "";
}
return String.valueOf(clipboard.hashCode());
}

/**
Expand Down Expand Up @@ -140,6 +90,8 @@ public void handleChunkData(Player player, String sessionId, int chunkIndex, byt
// 添加區塊數據
session.addChunk(chunkIndex, chunkData);

player.sendActionBar(mm.deserialize("<blue>Receiving clipboard... <gray>(" + session.getChunkCount() + "</gray>/<yellow>" + session.getTotalChunks() + "</yellow>)</blue>"));

// 檢查是否完成
if (session.isComplete()) {
handleCompleteTransfer(player, session);
Expand Down Expand Up @@ -183,6 +135,7 @@ private void handleCompleteTransfer(Player player, TransferSession session) {
}

public void uploadClipboard(Player player, byte[] data) {

try {
// 生成一個新的會話ID
String sessionId = UUID.randomUUID().toString();
Expand Down Expand Up @@ -212,15 +165,19 @@ public void uploadClipboard(Player player, byte[] data) {
out.writeInt(totalChunks);
out.writeInt(Constants.DEFAULT_CHUNK_SIZE);

// plugin.getLogger().info("PluginMessage: " + Constants.CHANNEL + ":" + "ClipboardUpload" + ":" + player.getUniqueId() + ":" + sessionId + ":" + totalChunks + ":" + Constants.DEFAULT_CHUNK_SIZE);

player.sendPluginMessage(plugin, Constants.CHANNEL, out.toByteArray());

// 發送區塊數據
sendChunks(player, sessionId, data, totalChunks);

Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
sendChunks(player, sessionId, data, totalChunks);
Bukkit.getScheduler().runTask(plugin, () -> {
plugin.getLogger().info("Uploaded clipboard for player: " + player.getName() + ", session: " + sessionId);
});
});
// sendChunks(player, sessionId, data, totalChunks);

// player.sendMessage("§e正在上傳剪貼簿...");
plugin.getLogger().info("Uploaded clipboard for player: " + player.getName() + ", session: " + sessionId);

} catch (Exception e) {
plugin.getLogger().severe("上傳剪貼簿時發生錯誤: " + e.getMessage());
Expand All @@ -229,6 +186,13 @@ public void uploadClipboard(Player player, byte[] data) {
}

private void sendChunks(Player player, String sessionId, byte[] data, int totalChunks) {
//wait 10ms
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.fillInStackTrace();
}

try {
for (int i = 0; i < totalChunks; i++) {
// 計算當前區塊的起始和結束位置
Expand Down Expand Up @@ -290,7 +254,6 @@ public boolean hasClipboardChanged(Player player, Clipboard currentClipboard) {

// 如果玩家沒有暫存的剪貼簿數據,視為有變化
if (!clipboardCache.containsKey(player.getUniqueId())) {
// plugin.getLogger().warning("玩家沒有暫存的剪貼簿數據");
return true;
}

Expand All @@ -302,21 +265,10 @@ public boolean hasClipboardChanged(Player player, Clipboard currentClipboard) {

// 如果任一雜湊值為空,視為有變化
if (currentHash.isEmpty() || storedHash.isEmpty()) {
// plugin.getLogger().warning("雜湊值為空 - 當前: " + currentHash + ", 儲存: " + storedHash);
return true;
}

// 比較雜湊值
boolean changed = !currentHash.equals(storedHash);

// 添加調試日誌
if (changed) {
// plugin.getLogger().warning("剪貼簿雜湊值不同:");
// plugin.getLogger().warning("當前: " + currentHash);
// plugin.getLogger().warning("儲存: " + storedHash);
}

return changed;
return !currentHash.equals(storedHash);

} catch (Exception e) {
plugin.getLogger().warning("An error occurred while checking clipboard changes: " + e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package dev.twme.worldeditsync.paper.worldedit;

import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitPlayer;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
@Plugin(
id = "worldeditsync",
name = "WorldEditSync",
version = "0.0.2",
version = "0.0.3",
description = "Sync WorldEdit clipboard across servers",
authors = {"TWME"}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public void createTransferSession(String sessionId, UUID playerUuid,
}

public void addChunk(String sessionId, int index, byte[] data) {

TransferSession session = transferSessions.get(sessionId);
if (session != null) {
// plugin.getLogger().info("Add chunk: {}", index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,40 +175,54 @@ private void sendClipboardData(Player player, byte[] data) {
)
);

// 發送數據塊
for (int i = 0; i < totalChunks; i++) {
int start = i * Constants.DEFAULT_CHUNK_SIZE;
int end = Math.min(start + Constants.DEFAULT_CHUNK_SIZE, data.length);
byte[] chunk = new byte[end - start];
System.arraycopy(data, start, chunk, 0, chunk.length);

ByteArrayDataOutput chunkOut = ByteStreams.newDataOutput();
chunkOut.writeUTF("ClipboardChunk");
chunkOut.writeUTF(sessionId);
chunkOut.writeInt(i + 1);
chunkOut.writeInt(chunk.length);
chunkOut.write(chunk);
// 發送數據塊 異步

plugin.getServer().getScheduler().buildTask(plugin, () -> {
String currentServer = player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse("null");
for (int i = 0; i < totalChunks; i++) {
if (currentServer.equals("null")) {
return;
}

String lastServer = player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse("null");
if (!lastServer.equals(currentServer)) {
return;
}

try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.fillInStackTrace();
}
int start = i * Constants.DEFAULT_CHUNK_SIZE;
int end = Math.min(start + Constants.DEFAULT_CHUNK_SIZE, data.length);
byte[] chunk = new byte[end - start];
System.arraycopy(data, start, chunk, 0, chunk.length);

ByteArrayDataOutput chunkOut = ByteStreams.newDataOutput();
chunkOut.writeUTF("ClipboardChunk");
chunkOut.writeUTF(sessionId);
chunkOut.writeInt(i + 1);
chunkOut.writeInt(chunk.length);
chunkOut.write(chunk);

// player.sendPluginMessage(
// MinecraftChannelIdentifier.from(Constants.CHANNEL),
// chunkOut.toByteArray()
// );

player.getCurrentServer().ifPresent(server ->
server.sendPluginMessage(
MinecraftChannelIdentifier.from(Constants.CHANNEL),
chunkOut.toByteArray()
)
);

if (i % 10 == 0 || i == totalChunks - 1) {
// plugin.getLogger().info("發送進度 - 玩家: {}, 會話: {}, 已發送: {}/{} 區塊", player.getUsername(), sessionId, i + 1, totalChunks);
player.getCurrentServer().ifPresent(server ->
server.sendPluginMessage(
MinecraftChannelIdentifier.from(Constants.CHANNEL),
chunkOut.toByteArray()
)
);
}
}
plugin.getLogger().info("Finished sending clipboard data to player: {} Session: {}", player.getUsername(), sessionId);

}).schedule();

plugin.getLogger().info("Finished sending clipboard data to player: {} Session: {}", player.getUsername(), sessionId);

// plugin.getLogger().info("完成向玩家 {} 發送剪貼板數據", player.getUsername());
}

private void sendClipboardInfo(Player player, String hash) {
Expand Down

0 comments on commit 16568fa

Please sign in to comment.