Skip to content

Commit

Permalink
bugfix:undo/redo多人协同问题修复
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaofeng committed Jul 28, 2021
1 parent e3700a7 commit 3b9f5ab
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 11 deletions.
122 changes: 118 additions & 4 deletions case-server/src/main/java/com/xiaoju/framework/handler/Room.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.flipkart.zjsonpatch.JsonDiff;
import com.flipkart.zjsonpatch.JsonPatch;
import com.xiaoju.framework.entity.persistent.TestCase;
import com.xiaoju.framework.mapper.TestCaseMapper;
import com.xiaoju.framework.service.CaseBackupService;
Expand All @@ -19,6 +23,9 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import static com.flipkart.zjsonpatch.DiffFlags.ADD_ORIGINAL_VALUE_ON_REPLACE;
import static com.flipkart.zjsonpatch.DiffFlags.OMIT_MOVE_OPERATION;

/**
* Created by didi on 2021/3/22.
*/
Expand All @@ -37,13 +44,20 @@ public abstract class Room {

private static final int MAX_PLAYER_COUNT = 100;
public final List<Player> players = new ArrayList<>();

public final List<String> undoDiffs = new LinkedList<>();
public final List<String> redoDiffs = new LinkedList<>();
private Integer undoPosition;
private Integer redoPosition;

public final Map<Session, Client> cs = new ConcurrentHashMap<>();

public static TestCaseMapper caseMapper;
public static RecordService recordService;
public static CaseBackupService caseBackupService;

ObjectMapper jsonMapper = new ObjectMapper();
JsonNodeFactory FACTORY = JsonNodeFactory.instance;

protected String testCaseContent;
protected TestCase testCase;
Expand Down Expand Up @@ -76,6 +90,8 @@ public Room(Long id) {
if (StringUtils.isEmpty(res)) {
LOGGER.error(Thread.currentThread().getName() + ": 用例内容为空");
}
undoPosition = undoDiffs.size();
redoPosition = redoDiffs.size();
}

private TimerTask createBroadcastTimerTask() {
Expand Down Expand Up @@ -158,8 +174,44 @@ private void internalHandleMessage(Player p, String msg,
p.setLastReceivedMessageId(msgId);

//todo: testCase.apply(msg) 新增如上的方法.
if (msg.endsWith("undo")) {
undo();
} else if (msg.endsWith("redo")) {
redo();
} else {
broadcastMessage(msg);
}
}

broadcastMessage(msg);
private void undo() {
roomLock.lock();
undoPosition --;
redoPosition --;
broadcastRoomMessage(CaseMessageType.EDITOR, undoDiffs.get(undoPosition));
try {
JsonNode target = JsonPatch.apply(jsonMapper.readTree(undoDiffs.get(undoPosition)), jsonMapper.readTree(testCaseContent));
testCaseContent = target.toString();
} catch (Exception e) {
roomLock.unlock();
LOGGER.error("undo json parse error。", e);
}
roomLock.unlock();
}

private void redo() {
roomLock.lock();
broadcastRoomMessage(CaseMessageType.EDITOR, redoDiffs.get(redoPosition));
try {
JsonNode target = JsonPatch.apply(jsonMapper.readTree(redoDiffs.get(undoPosition)), jsonMapper.readTree(testCaseContent));
testCaseContent = target.toString();
} catch (Exception e) {
roomLock.unlock();
LOGGER.error("redo json parse error。", e);
}

undoPosition ++;
redoPosition ++;
roomLock.unlock();
}

private void internalHandleCtrlMessage(String msg) {
Expand Down Expand Up @@ -193,14 +245,31 @@ private void broadcastMessage(String msg) {
JsonNode request = jsonMapper.readTree(msg.substring(seperateIndex + 1));
ArrayNode patch = (ArrayNode) request.get("patch");
long currentVersion = ((JsonNode) request.get("case")).get("base").asLong();
testCaseContent = ((JsonNode) request.get("case")).toString().replace("\"base\":" + currentVersion, "\"base\":" + (currentVersion + 1));
String tmpTestCaseContent = ((JsonNode) request.get("case")).toString().replace("\"base\":" + currentVersion, "\"base\":" + (currentVersion + 1));;
ArrayNode patchReverse = (ArrayNode) JsonDiff.asJson(jsonMapper.readTree(tmpTestCaseContent),
jsonMapper.readTree(testCaseContent==null?testCase.getCaseContent():testCaseContent), EnumSet.of(ADD_ORIGINAL_VALUE_ON_REPLACE, OMIT_MOVE_OPERATION));

testCaseContent = tmpTestCaseContent;
ArrayNode patchNew = patchTraverse(patch);

ObjectNode basePatch = FACTORY.objectNode();
basePatch.put("op", "replace");
basePatch.put("path", "/base");
basePatch.put("value", currentVersion + 1);
patchNew.add(basePatch);
// String msgNotify = patch.toString().replace("[[{", "[[{\"op\":\"replace\",\"path\":\"/base\",\"value\":" + (currentVersion + 1) + "},{");
redoDiffs.add(redoPosition++, patchNew.toString());
undoDiffs.add(undoPosition++, patchReverse.toString());

for (Player p : players) {
if (sendSessionId.equals(p.getClient().getSession().getId())) { //ack消息
String msgAck = "[[{\"op\":\"replace\",\"path\":\"/base\",\"value\":" + (currentVersion + 1) + "}]]";
p.getBufferedMessages().add(msgAck);
p.undoCount ++;
} else { // notify消息
String msgNotify = patch.toString().replace("[[{", "[[{\"op\":\"replace\",\"path\":\"/base\",\"value\":" + (currentVersion + 1) + "},{");
p.getBufferedMessages().add(msgNotify);
// String msgNotify = patch.toString().replace("[[{", "[[{\"op\":\"replace\",\"path\":\"/base\",\"value\":" + (currentVersion + 1) + "},{");
p.getBufferedMessages().add(patchNew.toString());
p.undoCount ++;
}
}
} catch (Exception e) {
Expand All @@ -209,6 +278,18 @@ private void broadcastMessage(String msg) {
}
}

private ArrayNode patchTraverse(ArrayNode patch) {
ArrayNode patchesNew = FACTORY.arrayNode();
try {
for (int i = 0; i < patch.size(); i++) {
patchesNew.addAll((ArrayNode) patch.get(i));
}
} catch (Exception e) {
LOGGER.error("转换客户端发送patch失败。", e);
}
return patchesNew;
}

private void broadcastTimerTick() {
// 给每一个player广播消息
for (Player p : players) {
Expand Down Expand Up @@ -299,6 +380,11 @@ public static final class Player {

private Integer pingCount;

// private Integer undoPosition;
// private Integer redoPosition;
private Integer undoCount;
private Integer redoCount;

// private final boolean isRecord;

/**
Expand All @@ -323,9 +409,37 @@ private Player(Room room, Client client) {
this.client = client;
this.enterTimeStamp = System.currentTimeMillis();
this.pingCount = 0;
// this.undoPosition = room.undoDiffs.size();
// this.redoPosition = room.redoDiffs.size();
this.undoCount = 0;
this.redoCount = 0;
// isRecord = client.getRecordId();
}

public Boolean undo() {
if (this.undoCount <= 0) {
LOGGER.warn("当前用户未编辑过,无法进行undo。用户是:" + this.client.getClientName());
return false;
}
this.undoCount --;
// this.undoPosition --;
this.redoCount ++;
// this.redoPosition --;
return true;
}

public Boolean redo() {
if (this.redoCount <= 0) {
LOGGER.warn("当前用户未undo过,无法进行redo。用户是:" + this.client.getClientName());
return false;
}
this.undoCount ++;
// this.undoPosition ++;
this.redoCount --;
// this.redoPosition ++;
return true;
}

public Room getRoom() {
return room;
}
Expand Down
2 changes: 1 addition & 1 deletion case-server/src/main/resources/web/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
</head>
<body>
<div id="root"></div>
<script th:src="@{/umi.2415d0c2.js}"></script>
<script th:src="@{/umi.c3201dac.js}"></script>
</body>
</html>

Large diffs are not rendered by default.

Loading

0 comments on commit 3b9f5ab

Please sign in to comment.