Added edit logging and whole world rollback

This commit is contained in:
Commodore64x
2018-05-12 18:34:44 +10:00
parent 26025a3018
commit 7f2c2d9d6b
13 changed files with 273 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.Application.ApplicationType;
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.TileEntity;
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.EffectEntity;
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<ArrayList<EditLog>> editLogs = new IntMap<>();
public static boolean headless = false;

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
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 class NetClient extends Module {
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));

View File

@@ -26,7 +26,6 @@ import io.anuke.ucore.util.Timer;
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 class NetServer extends Module{
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 class NetServer extends Module{
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 class NetServer extends Module{
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 @@ public class NetServer extends Module{
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);
}
}

View File

@@ -489,6 +489,12 @@ public class Renderer extends RendererModule{
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;

View File

@@ -25,6 +25,7 @@ public class DefaultKeybinds {
"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,

View File

@@ -107,6 +107,16 @@ public class DesktopInput extends InputHandler{
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;

View File

@@ -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<String, PlayerInfo> playerInfo = new ObjectMap<>();
/**Maps UUIDs to trace infos. This is wiped when a player logs off.*/
private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>();
/**Maps packed coordinates to logs for that coordinate */
private IntMap<ArrayList<EditLog>> editLogs = new IntMap<>();
private Array<String> bannedIPs = new Array<>();
public Administration(){
@@ -48,6 +60,22 @@ public class Administration {
Settings.save();
}
public IntMap<ArrayList<EditLog>> 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<EditLog> 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;

View File

@@ -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;
}
}

View File

@@ -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.resource.Item;
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 class Packets {
}
}
public static class BlockLogSyncPacket implements Packet {
public IntMap<ArrayList<EditLog>> editlogs;
@Override
public void write(ByteBuffer buffer) {
int size = editlogs.size;
buffer.putInt(size);
for(IntMap.Entry<ArrayList<EditLog>> editlog :editlogs.entries()) {
String key = String.valueOf(editlog.key);
buffer.put((byte) key.getBytes().length);
buffer.put(key.getBytes());
ArrayList<EditLog> 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<EditLog> 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;

View File

@@ -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<Class<?>> ids = new ObjectIntMap<>();

View File

@@ -5,8 +5,10 @@ import com.badlogic.gdx.graphics.Colors;
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.scene.ui.layout.Table;
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 class BlocksFragment implements Fragment{
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<EditLog> 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();