From 7f2c2d9d6bda1d3d4069c8bef20370b4c62ec994 Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Sat, 12 May 2018 18:34:44 +1000 Subject: [PATCH 1/6] Added edit logging and whole world rollback --- core/assets/bundles/bundle.properties | 2 + core/src/io/anuke/mindustry/Vars.java | 6 +- .../io/anuke/mindustry/core/NetClient.java | 5 ++ .../io/anuke/mindustry/core/NetServer.java | 15 +++- .../src/io/anuke/mindustry/core/Renderer.java | 6 ++ .../mindustry/input/DefaultKeybinds.java | 1 + .../anuke/mindustry/input/DesktopInput.java | 10 +++ .../anuke/mindustry/net/Administration.java | 28 ++++++++ core/src/io/anuke/mindustry/net/EditLog.java | 41 +++++++++++ core/src/io/anuke/mindustry/net/Packets.java | 51 +++++++++++++- .../io/anuke/mindustry/net/Registrator.java | 3 +- .../ui/fragments/BlocksFragment.java | 45 +++++++++++- .../anuke/mindustry/server/ServerControl.java | 68 +++++++++++++++++++ 13 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 core/src/io/anuke/mindustry/net/EditLog.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index e55b3502e787..86e5af012727 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -209,6 +209,8 @@ placemode.touch.name=touch placemode.cursor.name=cursor text.blocks.extrainfo=[accent]extra block info: text.blocks.blockinfo=Block Info +text.blocks.editlogs=Edit Logs +text.block.editlogsnotfound=[red]There are no edit logs for this location. text.blocks.powercapacity=Power Capacity text.blocks.powershot=Power/shot text.blocks.powersecond=Power/second diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index f7a3b756a295..eed8707e9448 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.core.*; import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.Player; @@ -11,6 +12,7 @@ import io.anuke.mindustry.entities.effect.Shield; import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.io.Platform; +import io.anuke.mindustry.net.EditLog; import io.anuke.mindustry.net.ClientDebug; import io.anuke.mindustry.net.ServerDebug; import io.anuke.ucore.UCore; @@ -18,7 +20,7 @@ import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.scene.ui.layout.Unit; - +import java.util.ArrayList; import java.util.Locale; public class Vars{ @@ -79,6 +81,8 @@ public class Vars{ public static boolean showUI = true; //whether to show block debug public static boolean showBlockDebug = false; + + public static IntMap> editLogs = new IntMap<>(); public static boolean headless = false; diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 2df2ef14c48d..c5fa2bbd347c 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.TimeUtils; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.BulletType; @@ -167,6 +168,10 @@ public NetClient(){ ui.hudfrag.updateItems(); }); + + Net.handleClient(BlockLogSyncPacket.class, packet -> { + Vars.editLogs = packet.editlogs; + }); Net.handleClient(PlacePacket.class, (packet) -> { Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, Timers.get("placeblocksound", 10)); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index ce48cc0827f9..5532a0dbb75d 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -26,7 +26,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; - import static io.anuke.mindustry.Vars.*; public class NetServer extends Module{ @@ -34,6 +33,7 @@ public class NetServer extends Module{ private final static int timerEntitySync = 0; private final static int timerStateSync = 1; + private final static int timerBlockLogSync = 2; public final Administration admins = new Administration(); @@ -212,6 +212,7 @@ public NetServer(){ Placement.placeBlock(packet.x, packet.y, block, packet.rotation, true, false); + admins.logEdit(packet.x, packet.y, connections.get(id), block, packet.rotation, EditLog.EditAction.PLACE); admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block; admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++; admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++; @@ -236,6 +237,7 @@ public NetServer(){ Block block = Placement.breakBlock(packet.x, packet.y, true, false); if(block != null) { + admins.logEdit(packet.x, packet.y, connections.get(id), block, tile.getRotation(), EditLog.EditAction.BREAK); admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block; admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++; admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++; @@ -313,7 +315,7 @@ public NetServer(){ packet.id = connections.get(id).id; Net.sendExcept(id, packet, SendMode.tcp); }); - + Net.handleServer(AdministerRequestPacket.class, (id, packet) -> { Player player = connections.get(id); @@ -475,7 +477,14 @@ void sync(){ packet.wave = state.wave; packet.time = Timers.time(); packet.timestamp = TimeUtils.millis(); - + + Net.send(packet, SendMode.udp); + } + + if(timer.get(timerBlockLogSync, serverSyncTime)) { + BlockLogSyncPacket packet = new BlockLogSyncPacket(); + packet.editlogs = admins.getEditLogs(); + Net.send(packet, SendMode.udp); } } diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index dbd1d3cb2039..214b5785c0ad 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -489,6 +489,12 @@ void drawOverlay(){ Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize); Draw.color(); } + + if(Inputs.keyDown("block_logs")){ + Draw.color(Colors.get("accent")); + Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize); + Draw.color(); + } if(target.entity != null) { int bot = 0, top = 0; diff --git a/core/src/io/anuke/mindustry/input/DefaultKeybinds.java b/core/src/io/anuke/mindustry/input/DefaultKeybinds.java index f17fcc437cc1..d35b60e229c2 100644 --- a/core/src/io/anuke/mindustry/input/DefaultKeybinds.java +++ b/core/src/io/anuke/mindustry/input/DefaultKeybinds.java @@ -25,6 +25,7 @@ public static void load(){ "rotate", new Axis(Input.SCROLL), "toggle_menus", Input.C, "block_info", Input.CONTROL_LEFT, + "block_logs", Input.I, "player_list", Input.TAB, "chat", Input.ENTER, "chat_history_prev", Input.UP, diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 1c7a3f68f35f..ef457f8b860b 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -107,6 +107,16 @@ public void update(){ Cursors.restoreCursor(); } } + + if(recipe == null && !ui.hasMouse() && Inputs.keyDown("block_logs")) { + showCursor = true; + if(Inputs.keyTap("select")){ + Timers.runTask(20f, () -> { + ui.hudfrag.blockfrag.showBlockLogs(getBlockX(), getBlockY()); + Cursors.restoreCursor(); + }); + } + } if(target != null && target.block().isConfigurable(target)){ showCursor = true; diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index ee2443deb7c2..2e59ed0bda30 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -1,10 +1,19 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.TimeUtils; +import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.blocks.types.BlockPart; +import io.anuke.mindustry.world.blocks.types.Floor; +import io.anuke.mindustry.world.blocks.types.Rock; +import io.anuke.mindustry.world.blocks.types.StaticBlock; import io.anuke.ucore.core.Settings; +import java.util.ArrayList; +import static io.anuke.mindustry.Vars.world; public class Administration { public static final int defaultMaxBrokenBlocks = 15; @@ -15,6 +24,9 @@ public class Administration { private ObjectMap playerInfo = new ObjectMap<>(); /**Maps UUIDs to trace infos. This is wiped when a player logs off.*/ private ObjectMap traceInfo = new ObjectMap<>(); + /**Maps packed coordinates to logs for that coordinate */ + private IntMap> editLogs = new IntMap<>(); + private Array bannedIPs = new Array<>(); public Administration(){ @@ -48,6 +60,22 @@ public void setAntiGriefParams(int maxBreak, int cooldown){ Settings.save(); } + public IntMap> getEditLogs() { + return editLogs; + } + + public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) { + if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; + if(editLogs.containsKey(x + y * world.width())) { + editLogs.get(x + y * world.width()).add(new EditLog(player, block, rotation, action)); + } + else { + ArrayList logs = new ArrayList<>(); + logs.add(new EditLog(player, block, rotation, action)); + editLogs.put(x + y * world.width(), logs); + } + } + public boolean validateBreak(String id, String ip){ if(!isAntiGrief() || isAdmin(id, ip)) return true; diff --git a/core/src/io/anuke/mindustry/net/EditLog.java b/core/src/io/anuke/mindustry/net/EditLog.java new file mode 100644 index 000000000000..036c1ee0b21f --- /dev/null +++ b/core/src/io/anuke/mindustry/net/EditLog.java @@ -0,0 +1,41 @@ +package io.anuke.mindustry.net; + +import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.world.Block; +import static io.anuke.mindustry.Vars.playerGroup; + +public class EditLog { + + public Player player; + public Block block; + public int rotation; + public EditAction action; + + EditLog(Player player, Block block, int rotation, EditAction action){ + this.player = player; + this.block = block; + this.rotation = rotation; + this.action = action; + } + + public String info() { + return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", player.name, block.name(), rotation, action.toString()); + } + + public static EditLog valueOf(String string) { + String[] parts = string.split(":"); + return new EditLog(playerGroup.getByID(Integer.valueOf(parts[0])), + Block.getByID(Integer.valueOf(parts[1])), + Integer.valueOf(parts[2]), + EditAction.valueOf(parts[3])); + } + + public String toString() { + return String.format("%s:%s:%s:%s", player.id, block.id, rotation, action.toString()); + } + + public enum EditAction{ + PLACE, + BREAK; + } +} diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index d7465cb7c913..98137941d909 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.utils.Base64Coder; +import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.entities.Player; @@ -12,8 +13,8 @@ import io.anuke.mindustry.world.Block; import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; - import java.nio.ByteBuffer; +import java.util.ArrayList; /**Class for storing all packets.*/ public class Packets { @@ -140,6 +141,54 @@ public void read(ByteBuffer buffer) { } } + public static class BlockLogSyncPacket implements Packet { + public IntMap> editlogs; + + @Override + public void write(ByteBuffer buffer) { + int size = editlogs.size; + buffer.putInt(size); + for(IntMap.Entry> editlog :editlogs.entries()) { + + String key = String.valueOf(editlog.key); + buffer.put((byte) key.getBytes().length); + buffer.put(key.getBytes()); + + ArrayList values = editlog.value; + buffer.putInt(values.size()); + for(EditLog value : values) { + + String rawValue = value.toString(); + buffer.put((byte) rawValue.getBytes().length); + buffer.put(rawValue.getBytes()); + } + } + } + + @Override + public void read(ByteBuffer buffer) { + editlogs = new IntMap<>(); + int logssize = buffer.getInt(); + for(int i = 0; i < logssize; i ++){ + + byte length = buffer.get(); + byte[] bytes = new byte[length]; + buffer.get(bytes); + Integer key = Integer.valueOf(new String(bytes)); + + ArrayList list = new ArrayList<>(); + int arraySize = buffer.getInt(); + for(int a = 0; a < arraySize; a ++) { + + byte[] arraybytes = new byte[buffer.get()]; + buffer.get(arraybytes); + list.add(EditLog.valueOf(new String(arraybytes))); + } + editlogs.put(key, list); + } + } + } + public static class PositionPacket implements Packet{ public byte[] data; diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 8e1dfa829727..54c97743a042 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -17,6 +17,7 @@ public class Registrator { PlacePacket.class, BreakPacket.class, StateSyncPacket.class, + BlockLogSyncPacket.class, BlockSyncPacket.class, BulletPacket.class, EnemyDeathPacket.class, @@ -44,7 +45,7 @@ public class Registrator { NetErrorPacket.class, PlayerAdminPacket.class, AdministerRequestPacket.class, - TracePacket.class, + TracePacket.class }; private static ObjectIntMap> ids = new ObjectIntMap<>(); diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java index fa1a178024ee..2690096267c9 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java @@ -5,8 +5,10 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.input.InputHandler; +import io.anuke.mindustry.net.EditLog; import io.anuke.mindustry.resource.*; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Block; @@ -24,7 +26,7 @@ import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Strings; - +import java.util.ArrayList; import static io.anuke.mindustry.Vars.*; public class BlocksFragment implements Fragment{ @@ -344,7 +346,46 @@ public void showBlockInfo(Block block){ d.show(); } - + + public void showBlockLogs(int x, int y){ + boolean wasPaused = state.is(State.paused); + state.set(State.paused); + + FloatingDialog d = new FloatingDialog("$text.blocks.editlogs"); + Table table = new Table(); + table.defaults().pad(1f); + ScrollPane pane = new ScrollPane(table, "clear"); + pane.setFadeScrollBars(false); + Table top = new Table(); + top.left(); + top.add("[accent]Edit logs for: "+ x + ", " + y); + table.add(top).fill().left(); + table.row(); + + d.content().add(pane).grow(); + + ArrayList logs = Vars.editLogs.get(x + y * world.width()); + if(logs == null || logs.isEmpty()) { + table.add("$text.block.editlogsnotfound").left(); + table.row(); + } + else { + for(int i = 0; i < logs.size(); i++) { + EditLog log = logs.get(i); + table.add("[gold]" + (i + 1) + ". [white]" + log.info()).left(); + table.row(); + } + } + + d.buttons().addButton("$text.ok", () -> { + if(!wasPaused) + state.set(State.playing); + d.hide(); + }).size(110, 50).pad(10f); + + d.show(); + } + public void updateItems(){ itemtable.clear(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 57a6cd25ac84..216b03e84322 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -3,6 +3,7 @@ import com.badlogic.gdx.ApplicationLogger; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.game.Difficulty; @@ -15,7 +16,9 @@ import io.anuke.mindustry.net.Packets.ChatPacket; import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.ui.fragments.DebugFragment; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Map; +import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.*; import io.anuke.ucore.modules.Module; @@ -27,6 +30,8 @@ import io.anuke.ucore.util.Strings; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Scanner; import static io.anuke.mindustry.Vars.*; @@ -723,6 +728,69 @@ private void registerCommands(){ info("Nobody with that name could be found."); } }); + + handler.register("rollback", "", "Rollback the block edits in the world", arg -> { + if(!state.is(State.playing)) { + err("Open the server first."); + return; + } + if(arg[0] == null) { + err("Please specify the amount of block edit cycles to rollback"); + return; + } + + int rollbackTimes = Integer.valueOf(arg[0]); + IntMap> editLogs = netServer.admins.getEditLogs(); + if(editLogs.size == 0){ + err("Nothing to rollback!"); + return; + } + + for(IntMap.Entry> editLog : editLogs.entries()) { + int coords = editLog.key; + ArrayList logs = editLog.value; + + for(int i = 0; i < rollbackTimes; i++) { + + EditLog log = logs.get(logs.size() - 1); + + int x = coords % world.width(); + int y = coords / world.width(); + Block result = log.block; + int rotation = log.rotation; + + if(log.action == EditLog.EditAction.PLACE) { + Placement.breakBlock(x, y, false, false); + + Packets.BreakPacket packet = new Packets.BreakPacket(); + packet.x = (short) x; + packet.y = (short) y; + packet.playerid = 0; + + Net.send(packet, Net.SendMode.tcp); + } + else if(log.action == EditLog.EditAction.BREAK) { + Placement.placeBlock(x, y, result, rotation, false, false); + + Packets.PlacePacket packet = new Packets.PlacePacket(); + packet.x = (short) x; + packet.y = (short) y; + packet.rotation = (byte) rotation; + packet.playerid = 0; + packet.block = result.id; + + Net.send(packet, Net.SendMode.tcp); + } + + logs.remove(logs.size() - 1); + if(logs.isEmpty()) { + editLogs.remove(coords); + break; + } + } + } + info("Rollback done!"); + }); } private void readCommands(){ From b3adf7b33160880dd45203a172e204698f7fd820 Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Mon, 14 May 2018 18:48:44 +1000 Subject: [PATCH 2/6] Replaced ArrayLists with Arrays, overhauled requesting of logs from the server, improved rollback command and added log resetting. --- core/src/io/anuke/mindustry/Vars.java | 6 +- .../io/anuke/mindustry/core/NetClient.java | 7 ++- .../io/anuke/mindustry/core/NetServer.java | 13 ++-- .../anuke/mindustry/input/DesktopInput.java | 1 + .../anuke/mindustry/net/Administration.java | 11 ++-- core/src/io/anuke/mindustry/net/EditLog.java | 13 ++++ .../src/io/anuke/mindustry/net/NetEvents.java | 9 +++ core/src/io/anuke/mindustry/net/Packets.java | 59 +++++++------------ .../io/anuke/mindustry/net/Registrator.java | 2 +- .../ui/fragments/BlocksFragment.java | 8 +-- .../anuke/mindustry/server/ServerControl.java | 21 +++---- 11 files changed, 78 insertions(+), 72 deletions(-) diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index eed8707e9448..d7165037e30c 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.core.*; import io.anuke.mindustry.entities.Bullet; @@ -20,7 +21,6 @@ import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.scene.ui.layout.Unit; -import java.util.ArrayList; import java.util.Locale; public class Vars{ @@ -81,8 +81,6 @@ public class Vars{ public static boolean showUI = true; //whether to show block debug public static boolean showBlockDebug = false; - - public static IntMap> editLogs = new IntMap<>(); public static boolean headless = false; @@ -94,6 +92,8 @@ public class Vars{ //amount of drops that are left when breaking a block public static final float breakDropAmount = 0.5f; + public static Array currentEditLogs = new Array<>(); + //only if smoothCamera public static boolean snapCamera = true; diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index c5fa2bbd347c..8f31cf8b632b 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -169,9 +169,9 @@ public NetClient(){ ui.hudfrag.updateItems(); }); - Net.handleClient(BlockLogSyncPacket.class, packet -> { - Vars.editLogs = packet.editlogs; - }); + Net.handleClient(BlockLogRequestPacket.class, packet -> { + currentEditLogs = packet.editlogs; + }); Net.handleClient(PlacePacket.class, (packet) -> { Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, Timers.get("placeblocksound", 10)); @@ -256,6 +256,7 @@ public NetClient(){ world.getCore().entity != null){ world.getCore().entity.onDeath(true); } + netServer.admins.getEditLogs().clear(); kicked = true; ui.restart.show(); }); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 5532a0dbb75d..da9f223cc798 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -33,7 +33,6 @@ public class NetServer extends Module{ private final static int timerEntitySync = 0; private final static int timerStateSync = 1; - private final static int timerBlockLogSync = 2; public final Administration admins = new Administration(); @@ -348,6 +347,11 @@ public NetServer(){ Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name); } }); + + Net.handleServer(BlockLogRequestPacket.class, (id, packet) -> { + packet.editlogs = EditLog.logsFromTile(packet.x, packet.y); + Net.sendTo(id, packet, SendMode.udp); + }); } public void update(){ @@ -480,12 +484,5 @@ void sync(){ Net.send(packet, SendMode.udp); } - - if(timer.get(timerBlockLogSync, serverSyncTime)) { - BlockLogSyncPacket packet = new BlockLogSyncPacket(); - packet.editlogs = admins.getEditLogs(); - - Net.send(packet, SendMode.udp); - } } } diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index ef457f8b860b..7564fae8aade 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -111,6 +111,7 @@ public void update(){ if(recipe == null && !ui.hasMouse() && Inputs.keyDown("block_logs")) { showCursor = true; if(Inputs.keyTap("select")){ + NetEvents.handleBlockLogRequest(getBlockX(), getBlockY()); Timers.runTask(20f, () -> { ui.hudfrag.blockfrag.showBlockLogs(getBlockX(), getBlockY()); Cursors.restoreCursor(); diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index 2e59ed0bda30..aa130ecb1960 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -12,7 +12,6 @@ import io.anuke.mindustry.world.blocks.types.Rock; import io.anuke.mindustry.world.blocks.types.StaticBlock; import io.anuke.ucore.core.Settings; -import java.util.ArrayList; import static io.anuke.mindustry.Vars.world; public class Administration { @@ -25,7 +24,7 @@ public class Administration { /**Maps UUIDs to trace infos. This is wiped when a player logs off.*/ private ObjectMap traceInfo = new ObjectMap<>(); /**Maps packed coordinates to logs for that coordinate */ - private IntMap> editLogs = new IntMap<>(); + private IntMap> editLogs = new IntMap<>(); private Array bannedIPs = new Array<>(); @@ -60,17 +59,21 @@ public void setAntiGriefParams(int maxBreak, int cooldown){ Settings.save(); } - public IntMap> getEditLogs() { + public IntMap> getEditLogs() { return editLogs; } + public void setEditLogs(IntMap> editLogs) { + this.editLogs = editLogs; + } + public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) { if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; if(editLogs.containsKey(x + y * world.width())) { editLogs.get(x + y * world.width()).add(new EditLog(player, block, rotation, action)); } else { - ArrayList logs = new ArrayList<>(); + Array logs = new Array<>(); logs.add(new EditLog(player, block, rotation, action)); editLogs.put(x + y * world.width(), logs); } diff --git a/core/src/io/anuke/mindustry/net/EditLog.java b/core/src/io/anuke/mindustry/net/EditLog.java index 036c1ee0b21f..e381f3eccdd1 100644 --- a/core/src/io/anuke/mindustry/net/EditLog.java +++ b/core/src/io/anuke/mindustry/net/EditLog.java @@ -1,8 +1,12 @@ package io.anuke.mindustry.net; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.world.Block; +import static io.anuke.mindustry.Vars.netServer; import static io.anuke.mindustry.Vars.playerGroup; +import static io.anuke.mindustry.Vars.world; public class EditLog { @@ -30,6 +34,15 @@ public static EditLog valueOf(String string) { EditAction.valueOf(parts[3])); } + public static Array logsFromTile(int x, int y) { + for(IntMap.Entry> editLog : netServer.admins.getEditLogs().entries()) { + if(editLog.key == (x + y * world.width())) { + return editLog.value; + } + } + return new Array<>(); + } + public String toString() { return String.format("%s:%s:%s:%s", player.id, block.id, rotation, action.toString()); } diff --git a/core/src/io/anuke/mindustry/net/NetEvents.java b/core/src/io/anuke/mindustry/net/NetEvents.java index ccb59f36a15c..bf4884c38171 100644 --- a/core/src/io/anuke/mindustry/net/NetEvents.java +++ b/core/src/io/anuke/mindustry/net/NetEvents.java @@ -177,4 +177,13 @@ public static void handleTraceRequest(Player target){ ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address)); } } + + public static void handleBlockLogRequest(int x, int y) { + BlockLogRequestPacket packet = new BlockLogRequestPacket(); + packet.x = x; + packet.y = y; + packet.editlogs = Vars.currentEditLogs; + + Net.send(packet, SendMode.udp); + } } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 98137941d909..6acbd1d3db0b 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -1,5 +1,6 @@ package io.anuke.mindustry.net; +import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.reflect.ClassReflection; @@ -14,7 +15,6 @@ import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; import java.nio.ByteBuffer; -import java.util.ArrayList; /**Class for storing all packets.*/ public class Packets { @@ -140,51 +140,36 @@ public void read(ByteBuffer buffer) { timestamp = buffer.getLong(); } } - - public static class BlockLogSyncPacket implements Packet { - public IntMap> editlogs; + public static class BlockLogRequestPacket implements Packet { + public int x; + public int y; + public Array editlogs; + @Override public void write(ByteBuffer buffer) { - int size = editlogs.size; - buffer.putInt(size); - for(IntMap.Entry> editlog :editlogs.entries()) { - - String key = String.valueOf(editlog.key); - buffer.put((byte) key.getBytes().length); - buffer.put(key.getBytes()); + buffer.putInt(x); + buffer.putInt(y); + buffer.putInt(editlogs.size); + for(EditLog value : editlogs) { - ArrayList values = editlog.value; - buffer.putInt(values.size()); - for(EditLog value : values) { - - String rawValue = value.toString(); - buffer.put((byte) rawValue.getBytes().length); - buffer.put(rawValue.getBytes()); - } + String rawValue = value.toString(); + buffer.put((byte) rawValue.getBytes().length); + buffer.put(rawValue.getBytes()); } } - + @Override public void read(ByteBuffer buffer) { - editlogs = new IntMap<>(); - int logssize = buffer.getInt(); - for(int i = 0; i < logssize; i ++){ - - byte length = buffer.get(); - byte[] bytes = new byte[length]; - buffer.get(bytes); - Integer key = Integer.valueOf(new String(bytes)); - - ArrayList list = new ArrayList<>(); - int arraySize = buffer.getInt(); - for(int a = 0; a < arraySize; a ++) { + x = buffer.getInt(); + y = buffer.getInt(); + editlogs = new Array<>(); + int arraySize = buffer.getInt(); + for(int a = 0; a < arraySize; a ++) { - byte[] arraybytes = new byte[buffer.get()]; - buffer.get(arraybytes); - list.add(EditLog.valueOf(new String(arraybytes))); - } - editlogs.put(key, list); + byte[] arraybytes = new byte[buffer.get()]; + buffer.get(arraybytes); + editlogs.add(EditLog.valueOf(new String(arraybytes))); } } } diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 54c97743a042..65b83f0066f0 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -17,7 +17,7 @@ public class Registrator { PlacePacket.class, BreakPacket.class, StateSyncPacket.class, - BlockLogSyncPacket.class, + BlockLogRequestPacket.class, BlockSyncPacket.class, BulletPacket.class, EnemyDeathPacket.class, diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java index 2690096267c9..e8bd7b388aeb 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java @@ -9,6 +9,7 @@ import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.net.EditLog; +import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.resource.*; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Block; @@ -26,7 +27,6 @@ import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Strings; -import java.util.ArrayList; import static io.anuke.mindustry.Vars.*; public class BlocksFragment implements Fragment{ @@ -364,13 +364,13 @@ public void showBlockLogs(int x, int y){ d.content().add(pane).grow(); - ArrayList logs = Vars.editLogs.get(x + y * world.width()); - if(logs == null || logs.isEmpty()) { + Array logs = Vars.currentEditLogs; + if(logs == null || logs.size == 0) { table.add("$text.block.editlogsnotfound").left(); table.row(); } else { - for(int i = 0; i < logs.size(); i++) { + for(int i = 0; i < logs.size; i++) { EditLog log = logs.get(i); table.add("[gold]" + (i + 1) + ". [white]" + log.info()).left(); table.row(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 216b03e84322..3dbcb922a1e2 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -30,15 +30,11 @@ import io.anuke.ucore.util.Strings; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Scanner; import static io.anuke.mindustry.Vars.*; import static io.anuke.ucore.util.Log.*; -; - public class ServerControl extends Module { private final CommandHandler handler = new CommandHandler(""); private ShuffleMode mode; @@ -734,25 +730,26 @@ private void registerCommands(){ err("Open the server first."); return; } - if(arg[0] == null) { - err("Please specify the amount of block edit cycles to rollback"); + + if(!Strings.canParsePostiveInt(arg[0])) { + err("Please input a valid, positive, number of times to rollback"); return; } int rollbackTimes = Integer.valueOf(arg[0]); - IntMap> editLogs = netServer.admins.getEditLogs(); + IntMap> editLogs = netServer.admins.getEditLogs(); if(editLogs.size == 0){ err("Nothing to rollback!"); return; } - for(IntMap.Entry> editLog : editLogs.entries()) { + for(IntMap.Entry> editLog : editLogs.entries()) { int coords = editLog.key; - ArrayList logs = editLog.value; + Array logs = editLog.value; for(int i = 0; i < rollbackTimes; i++) { - EditLog log = logs.get(logs.size() - 1); + EditLog log = logs.get(logs.size - 1); int x = coords % world.width(); int y = coords / world.width(); @@ -782,8 +779,8 @@ else if(log.action == EditLog.EditAction.BREAK) { Net.send(packet, Net.SendMode.tcp); } - logs.remove(logs.size() - 1); - if(logs.isEmpty()) { + logs.removeIndex(logs.size - 1); + if(logs.size == 0) { editLogs.remove(coords); break; } From 380a955e7abe67a2ee9478bd9c203610f298b674 Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Tue, 15 May 2018 16:36:39 +1000 Subject: [PATCH 3/6] Updated the way packets are exchanged for edit logs. --- .../io/anuke/mindustry/core/NetClient.java | 1 - .../io/anuke/mindustry/core/NetServer.java | 7 ++-- .../anuke/mindustry/net/Administration.java | 4 --- core/src/io/anuke/mindustry/net/EditLog.java | 34 ++----------------- core/src/io/anuke/mindustry/net/Packets.java | 27 ++++++++------- 5 files changed, 23 insertions(+), 50 deletions(-) diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 8f31cf8b632b..d36fd0fdccec 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -256,7 +256,6 @@ public NetClient(){ world.getCore().entity != null){ world.getCore().entity.onDeath(true); } - netServer.admins.getEditLogs().clear(); kicked = true; ui.restart.show(); }); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index da9f223cc798..08b46d282f54 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -44,7 +44,10 @@ public class NetServer extends Module{ public NetServer(){ - Events.on(GameOverEvent.class, () -> weapons.clear()); + Events.on(GameOverEvent.class, () -> { + weapons.clear(); + admins.getEditLogs().clear(); + }); Net.handleServer(Connect.class, (id, connect) -> { if(admins.isIPBanned(connect.addressTCP)){ @@ -349,7 +352,7 @@ public NetServer(){ }); Net.handleServer(BlockLogRequestPacket.class, (id, packet) -> { - packet.editlogs = EditLog.logsFromTile(packet.x, packet.y); + packet.editlogs = admins.getEditLogs().get(packet.x + packet.y * world.width(), new Array<>()); Net.sendTo(id, packet, SendMode.udp); }); } diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index aa130ecb1960..762f454dc0f7 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -63,10 +63,6 @@ public IntMap> getEditLogs() { return editLogs; } - public void setEditLogs(IntMap> editLogs) { - this.editLogs = editLogs; - } - public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) { if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; if(editLogs.containsKey(x + y * world.width())) { diff --git a/core/src/io/anuke/mindustry/net/EditLog.java b/core/src/io/anuke/mindustry/net/EditLog.java index e381f3eccdd1..2c8b0f76bcef 100644 --- a/core/src/io/anuke/mindustry/net/EditLog.java +++ b/core/src/io/anuke/mindustry/net/EditLog.java @@ -1,21 +1,15 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.world.Block; -import static io.anuke.mindustry.Vars.netServer; -import static io.anuke.mindustry.Vars.playerGroup; -import static io.anuke.mindustry.Vars.world; public class EditLog { - public Player player; public Block block; public int rotation; public EditAction action; - EditLog(Player player, Block block, int rotation, EditAction action){ + EditLog(Player player, Block block, int rotation, EditAction action) { this.player = player; this.block = block; this.rotation = rotation; @@ -26,29 +20,7 @@ public String info() { return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", player.name, block.name(), rotation, action.toString()); } - public static EditLog valueOf(String string) { - String[] parts = string.split(":"); - return new EditLog(playerGroup.getByID(Integer.valueOf(parts[0])), - Block.getByID(Integer.valueOf(parts[1])), - Integer.valueOf(parts[2]), - EditAction.valueOf(parts[3])); - } - - public static Array logsFromTile(int x, int y) { - for(IntMap.Entry> editLog : netServer.admins.getEditLogs().entries()) { - if(editLog.key == (x + y * world.width())) { - return editLog.value; - } - } - return new Array<>(); - } - - public String toString() { - return String.format("%s:%s:%s:%s", player.id, block.id, rotation, action.toString()); - } - - public enum EditAction{ - PLACE, - BREAK; + public enum EditAction { + PLACE, BREAK; } } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 6acbd1d3db0b..efcdb31299b0 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ReflectionException; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.io.Version; @@ -148,28 +149,30 @@ public static class BlockLogRequestPacket implements Packet { @Override public void write(ByteBuffer buffer) { - buffer.putInt(x); - buffer.putInt(y); + buffer.putShort((short)x); + buffer.putShort((short)y); buffer.putInt(editlogs.size); for(EditLog value : editlogs) { - - String rawValue = value.toString(); - buffer.put((byte) rawValue.getBytes().length); - buffer.put(rawValue.getBytes()); + buffer.putInt(value.player.id); + buffer.putInt(value.block.id); + buffer.put((byte) value.rotation); + buffer.put((byte) value.action.ordinal()); } } @Override public void read(ByteBuffer buffer) { - x = buffer.getInt(); - y = buffer.getInt(); + x = buffer.getShort(); + y = buffer.getShort(); editlogs = new Array<>(); int arraySize = buffer.getInt(); for(int a = 0; a < arraySize; a ++) { - - byte[] arraybytes = new byte[buffer.get()]; - buffer.get(arraybytes); - editlogs.add(EditLog.valueOf(new String(arraybytes))); + int playerid = buffer.getInt(); + int blockid = buffer.getInt(); + int rotation = buffer.get(); + int ordinal = buffer.get(); + + editlogs.add(new EditLog(Vars.playerGroup.getByID(playerid), Block.getByID(blockid), rotation, EditLog.EditAction.values()[ordinal])); } } } From 68af32bc9f903dfb99aa895fdf02d6b11521ed8c Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Sat, 19 May 2018 13:37:44 +1000 Subject: [PATCH 4/6] Added rollback into the admin menu, fixed a bug where players would crash upon trying to view their own logs on relog. --- core/assets/bundles/bundle.properties | 2 + .../io/anuke/mindustry/core/NetServer.java | 13 +++++ core/src/io/anuke/mindustry/core/UI.java | 2 + .../anuke/mindustry/net/Administration.java | 51 ++++++++++++++++++- core/src/io/anuke/mindustry/net/EditLog.java | 8 +-- .../src/io/anuke/mindustry/net/NetEvents.java | 7 +++ core/src/io/anuke/mindustry/net/Packets.java | 25 +++++++-- .../io/anuke/mindustry/net/Registrator.java | 1 + .../mindustry/ui/dialogs/RollbackDialog.java | 41 +++++++++++++++ .../ui/fragments/BlocksFragment.java | 9 ++-- .../ui/fragments/PlayerListFragment.java | 4 ++ .../anuke/mindustry/server/ServerControl.java | 44 +--------------- 12 files changed, 149 insertions(+), 58 deletions(-) create mode 100644 core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 86e5af012727..8929473e30ab 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -70,6 +70,8 @@ text.server.bans=Bans text.server.bans.none=No banned players found! text.server.admins=Admins text.server.admins.none=No admins found! +text.server.rollback=Rollback +text.server.rollback.numberfield=Rollback Amount: text.server.add=Add Server text.server.delete=Are you sure you want to delete this server? text.server.hostname=Host: {0} diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 08b46d282f54..84ba7bc83c20 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -355,6 +355,19 @@ public NetServer(){ packet.editlogs = admins.getEditLogs().get(packet.x + packet.y * world.width(), new Array<>()); Net.sendTo(id, packet, SendMode.udp); }); + + Net.handleServer(RollbackRequestPacket.class, (id, packet) -> { + Player player = connections.get(id); + + if(!player.isAdmin){ + Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform a rollback without proper security access.", + player.name, Net.getConnection(player.clientid).address); + return; + } + + admins.rollbackWorld(packet.rollbackTimes); + Log.info("&lc{0} has rolled back the world {1} times.", player.name, packet.rollbackTimes); + }); } public void update(){ diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index fe28e973cdf3..d2c2fd83dff9 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -49,6 +49,7 @@ public class UI extends SceneModule{ public BansDialog bans; public AdminsDialog admins; public TraceDialog traces; + public RollbackDialog rollback; public ChangelogDialog changelog; public final MenuFragment menufrag = new MenuFragment(); @@ -160,6 +161,7 @@ public void init(){ bans = new BansDialog(); admins = new AdminsDialog(); traces = new TraceDialog(); + rollback = new RollbackDialog(); build.begin(scene); diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index 762f454dc0f7..cd412e9c25a8 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -7,6 +7,7 @@ import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.blocks.types.BlockPart; import io.anuke.mindustry.world.blocks.types.Floor; import io.anuke.mindustry.world.blocks.types.Rock; @@ -66,15 +67,61 @@ public IntMap> getEditLogs() { public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) { if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; if(editLogs.containsKey(x + y * world.width())) { - editLogs.get(x + y * world.width()).add(new EditLog(player, block, rotation, action)); + editLogs.get(x + y * world.width()).add(new EditLog(player.name, block, rotation, action)); } else { Array logs = new Array<>(); - logs.add(new EditLog(player, block, rotation, action)); + logs.add(new EditLog(player.name, block, rotation, action)); editLogs.put(x + y * world.width(), logs); } } + public void rollbackWorld(int rollbackTimes) { + for(IntMap.Entry> editLog : editLogs.entries()) { + int coords = editLog.key; + Array logs = editLog.value; + + for(int i = 0; i < rollbackTimes; i++) { + + EditLog log = logs.get(logs.size - 1); + + int x = coords % world.width(); + int y = coords / world.width(); + Block result = log.block; + int rotation = log.rotation; + + if(log.action == EditLog.EditAction.PLACE) { + Placement.breakBlock(x, y, false, false); + + Packets.BreakPacket packet = new Packets.BreakPacket(); + packet.x = (short) x; + packet.y = (short) y; + packet.playerid = 0; + + Net.send(packet, Net.SendMode.tcp); + } + else if(log.action == EditLog.EditAction.BREAK) { + Placement.placeBlock(x, y, result, rotation, false, false); + + Packets.PlacePacket packet = new Packets.PlacePacket(); + packet.x = (short) x; + packet.y = (short) y; + packet.rotation = (byte) rotation; + packet.playerid = 0; + packet.block = result.id; + + Net.send(packet, Net.SendMode.tcp); + } + + logs.removeIndex(logs.size - 1); + if(logs.size == 0) { + editLogs.remove(coords); + break; + } + } + } + } + public boolean validateBreak(String id, String ip){ if(!isAntiGrief() || isAdmin(id, ip)) return true; diff --git a/core/src/io/anuke/mindustry/net/EditLog.java b/core/src/io/anuke/mindustry/net/EditLog.java index 2c8b0f76bcef..e9bbeec04395 100644 --- a/core/src/io/anuke/mindustry/net/EditLog.java +++ b/core/src/io/anuke/mindustry/net/EditLog.java @@ -4,20 +4,20 @@ import io.anuke.mindustry.world.Block; public class EditLog { - public Player player; + public String playername; public Block block; public int rotation; public EditAction action; - EditLog(Player player, Block block, int rotation, EditAction action) { - this.player = player; + EditLog(String playername, Block block, int rotation, EditAction action) { + this.playername = playername; this.block = block; this.rotation = rotation; this.action = action; } public String info() { - return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", player.name, block.name(), rotation, action.toString()); + return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", playername, block.name(), rotation, action.toString()); } public enum EditAction { diff --git a/core/src/io/anuke/mindustry/net/NetEvents.java b/core/src/io/anuke/mindustry/net/NetEvents.java index bf4884c38171..c9603e92fc13 100644 --- a/core/src/io/anuke/mindustry/net/NetEvents.java +++ b/core/src/io/anuke/mindustry/net/NetEvents.java @@ -186,4 +186,11 @@ public static void handleBlockLogRequest(int x, int y) { Net.send(packet, SendMode.udp); } + + public static void handleRollbackRequest(int rollbackTimes) { + RollbackRequestPacket packet = new RollbackRequestPacket(); + packet.rollbackTimes = rollbackTimes; + + Net.send(packet, SendMode.udp); + } } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index efcdb31299b0..bd0b8b9d3959 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -153,7 +153,8 @@ public void write(ByteBuffer buffer) { buffer.putShort((short)y); buffer.putInt(editlogs.size); for(EditLog value : editlogs) { - buffer.putInt(value.player.id); + buffer.put((byte)value.playername.getBytes().length); + buffer.put(value.playername.getBytes()); buffer.putInt(value.block.id); buffer.put((byte) value.rotation); buffer.put((byte) value.action.ordinal()); @@ -167,16 +168,34 @@ public void read(ByteBuffer buffer) { editlogs = new Array<>(); int arraySize = buffer.getInt(); for(int a = 0; a < arraySize; a ++) { - int playerid = buffer.getInt(); + byte length = buffer.get(); + byte[] bytes = new byte[length]; + buffer.get(bytes); + String name = new String(bytes); + int blockid = buffer.getInt(); int rotation = buffer.get(); int ordinal = buffer.get(); - editlogs.add(new EditLog(Vars.playerGroup.getByID(playerid), Block.getByID(blockid), rotation, EditLog.EditAction.values()[ordinal])); + editlogs.add(new EditLog(name, Block.getByID(blockid), rotation, EditLog.EditAction.values()[ordinal])); } } } + public static class RollbackRequestPacket implements Packet { + public int rollbackTimes; + + @Override + public void write(ByteBuffer buffer) { + buffer.putInt(rollbackTimes); + } + + @Override + public void read(ByteBuffer buffer) { + rollbackTimes = buffer.getInt(); + } + } + public static class PositionPacket implements Packet{ public byte[] data; diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 65b83f0066f0..92d6eca4b879 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -18,6 +18,7 @@ public class Registrator { BreakPacket.class, StateSyncPacket.class, BlockLogRequestPacket.class, + RollbackRequestPacket.class, BlockSyncPacket.class, BulletPacket.class, EnemyDeathPacket.class, diff --git a/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java new file mode 100644 index 000000000000..a581c183a719 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java @@ -0,0 +1,41 @@ +package io.anuke.mindustry.ui.dialogs; + +import io.anuke.mindustry.net.NetEvents; +import io.anuke.ucore.scene.ui.Label; +import io.anuke.ucore.scene.ui.TextField; +import io.anuke.ucore.scene.ui.layout.Table; +import io.anuke.ucore.util.Strings; +import static io.anuke.mindustry.Vars.*; + +public class RollbackDialog extends FloatingDialog { + + public RollbackDialog(){ + super("$text.server.rollback"); + + setup(); + shown(this::setup); + } + + private void setup(){ + content().clear(); + buttons().clear(); + + if(gwt) return; + + content().row(); + content().add("$text.server.rollback.numberfield"); + + TextField field = content().addField("", t->{}).get(); + field.setTextFieldFilter((f, c) -> field.getText().length() < 4); + content().add(field).size(220f, 48f); + + content().row(); + buttons().defaults().size(50f, 50f).left().pad(2f); + buttons().addButton("$text.cancel", this::hide); + + buttons().addButton("$text.ok", () -> { + NetEvents.handleRollbackRequest(Integer.valueOf(field.getText())); + hide(); + }).disabled(b -> field.getText().isEmpty() || !Strings.canParsePostiveInt(field.getText())); + } +} diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java index e8bd7b388aeb..67cb96cd38da 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java @@ -5,11 +5,9 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.net.EditLog; -import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.resource.*; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Block; @@ -364,14 +362,13 @@ public void showBlockLogs(int x, int y){ d.content().add(pane).grow(); - Array logs = Vars.currentEditLogs; - if(logs == null || logs.size == 0) { + if(currentEditLogs == null || currentEditLogs.size == 0) { table.add("$text.block.editlogsnotfound").left(); table.row(); } else { - for(int i = 0; i < logs.size; i++) { - EditLog log = logs.get(i); + for(int i = 0; i < currentEditLogs.size; i++) { + EditLog log = currentEditLogs.get(i); table.add("[gold]" + (i + 1) + ". [white]" + log.info()).left(); table.row(); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java index a636ba545acb..8e0aaf9323db 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlayerListFragment.java @@ -58,6 +58,10 @@ public void build(){ new button("$text.server.admins", () -> { ui.admins.show(); }).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client()); + + new button("$text.server.rollback", () -> { + ui.rollback.show(); + }).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> !player.isAdmin); }}.pad(10f).growX().end(); }}.end(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 3dbcb922a1e2..143e22b7ef8b 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -743,49 +743,7 @@ private void registerCommands(){ return; } - for(IntMap.Entry> editLog : editLogs.entries()) { - int coords = editLog.key; - Array logs = editLog.value; - - for(int i = 0; i < rollbackTimes; i++) { - - EditLog log = logs.get(logs.size - 1); - - int x = coords % world.width(); - int y = coords / world.width(); - Block result = log.block; - int rotation = log.rotation; - - if(log.action == EditLog.EditAction.PLACE) { - Placement.breakBlock(x, y, false, false); - - Packets.BreakPacket packet = new Packets.BreakPacket(); - packet.x = (short) x; - packet.y = (short) y; - packet.playerid = 0; - - Net.send(packet, Net.SendMode.tcp); - } - else if(log.action == EditLog.EditAction.BREAK) { - Placement.placeBlock(x, y, result, rotation, false, false); - - Packets.PlacePacket packet = new Packets.PlacePacket(); - packet.x = (short) x; - packet.y = (short) y; - packet.rotation = (byte) rotation; - packet.playerid = 0; - packet.block = result.id; - - Net.send(packet, Net.SendMode.tcp); - } - - logs.removeIndex(logs.size - 1); - if(logs.size == 0) { - editLogs.remove(coords); - break; - } - } - } + netServer.admins.rollbackWorld(rollbackTimes); info("Rollback done!"); }); } From 5a77eebb34bec3fbceff2cd9dd892e1c475fb14b Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Sat, 19 May 2018 14:22:07 +1000 Subject: [PATCH 5/6] Fixed ui in the rollback menu. --- core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java index a581c183a719..5a8139063fa9 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java @@ -25,12 +25,11 @@ private void setup(){ content().row(); content().add("$text.server.rollback.numberfield"); - TextField field = content().addField("", t->{}).get(); + TextField field = content().addField("", t->{}).size(60f, 48f).get(); field.setTextFieldFilter((f, c) -> field.getText().length() < 4); - content().add(field).size(220f, 48f); content().row(); - buttons().defaults().size(50f, 50f).left().pad(2f); + buttons().defaults().size(200f, 50f).left().pad(2f); buttons().addButton("$text.cancel", this::hide); buttons().addButton("$text.ok", () -> { From a3268eefbf895326ccff35898cea0600a36b5bc1 Mon Sep 17 00:00:00 2001 From: Commodore64x Date: Sat, 19 May 2018 14:34:50 +1000 Subject: [PATCH 6/6] Fixed field size in rollback menu. --- core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java index 5a8139063fa9..751ddcd9fc34 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/RollbackDialog.java @@ -25,7 +25,7 @@ private void setup(){ content().row(); content().add("$text.server.rollback.numberfield"); - TextField field = content().addField("", t->{}).size(60f, 48f).get(); + TextField field = content().addField("", t->{}).size(200f, 48f).get(); field.setTextFieldFilter((f, c) -> field.getText().length() < 4); content().row();