Fixed many network-related bugs

This commit is contained in:
Anuken
2018-01-28 17:09:21 -05:00
parent 205c4e723a
commit 3562a70669
26 changed files with 284 additions and 79 deletions

View File

@@ -9,6 +9,8 @@ import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.effect.Shield;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.net.ClientDebug;
import io.anuke.mindustry.net.ServerDebug;
import io.anuke.ucore.UCore;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup;
@@ -47,8 +49,9 @@ public class Vars{
//how much the zoom changes every zoom button press
public static final int zoomScale = Math.round(Unit.dp.scl(1));
//if true, player speed will be increased, massive amounts of resources will be given on start, and other debug options will be available
public static boolean debug = false;
public static boolean debugNet = false;
public static boolean debug = true;
public static boolean debugNet = true;
public static boolean console = false;
//whether the player can clip through walls
public static boolean noclip = false;
//whether to draw chunk borders
@@ -83,6 +86,9 @@ public class Vars{
public static final GameState state = new GameState();
public static final ServerDebug serverDebug = new ServerDebug();
public static final ClientDebug clientDebug = new ClientDebug();
public static Control control;
public static Logic logic;
public static Renderer renderer;

View File

@@ -4,7 +4,7 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.DefaultKeybinds;
import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Tutorial;
import io.anuke.mindustry.game.UpgradeInventory;
@@ -266,6 +266,10 @@ public class Control extends Module{
Gdx.input = proxy;
}
if(Inputs.keyTap("console")){
console = !console;
}
if(KeyBinds.getSection("default").device.type == DeviceType.controller){
if(Inputs.keyTap("select")){
Inputs.getProcessor().touchDown(Gdx.input.getX(), Gdx.input.getY(), 0, Buttons.LEFT);

View File

@@ -131,7 +131,7 @@ public class Logic extends Module {
}
}
if(state.wavetime <= 0 || state.extrawavetime <= 0){
if(!Net.client() && (state.wavetime <= 0 || state.extrawavetime <= 0)){
runWave();
}

View File

@@ -114,6 +114,8 @@ public class NetClient extends Module {
Net.handleClient(SyncPacket.class, packet -> {
if (!gotData) return;
int players = 0;
int enemies = 0;
ByteBuffer data = ByteBuffer.wrap(packet.data);
@@ -126,9 +128,11 @@ public class NetClient extends Module {
SyncEntity entity = (SyncEntity) group.getByID(id);
if(entity instanceof Player) players ++;
if(entity instanceof Enemy) enemies ++;
if (entity == null || id == player.id) {
if (id != player.id) {
Log.info("Requesting entity {0}, group {1}.", id, group.getType().toString().replace("class io.anuke.mindustry.entities.", ""));
EntityRequestPacket req = new EntityRequestPacket();
req.id = id;
Net.send(req, SendMode.udp);
@@ -138,6 +142,10 @@ public class NetClient extends Module {
entity.read(data);
}
}
if(debugNet){
clientDebug.setSyncDebug(players, enemies);
}
});
Net.handleClient(StateSyncPacket.class, packet -> {
@@ -161,6 +169,7 @@ public class NetClient extends Module {
recieved.add(spawn.id);
Enemy enemy = new Enemy(EnemyType.getByID(spawn.type));
enemy.interpolator.target.set(spawn.x, spawn.y);
enemy.set(spawn.x, spawn.y);
enemy.tier = spawn.tier;
enemy.lane = spawn.lane;
@@ -169,6 +178,8 @@ public class NetClient extends Module {
enemy.add();
Effects.effect(Fx.spawn, enemy);
Log.info("Recieved enemy {0}", spawn.id);
});
Net.handleClient(EnemyDeathPacket.class, spawn -> {
@@ -264,6 +275,8 @@ public class NetClient extends Module {
player.interpolator.target.set(player.x, player.y);
player.add();
Log.info("Recieved player {0}", packet.id);
Platform.instance.updateRPC();
});
@@ -316,6 +329,11 @@ public class NetClient extends Module {
return name == null ? null : "[#" + colorArray[id % colorArray.length].toString().toUpperCase() + "]" + name;
}
public void clearRecieved(){
recieved.clear();
dead.clear();
}
void sync(){
if(Timers.get("syncPlayer", playerSyncTime)){
byte[] bytes = new byte[player.getWriteSize()];

View File

@@ -157,7 +157,7 @@ public class NetServer extends Module{
Net.handleServer(WeaponSwitchPacket.class, (id, packet) -> {
packet.playerid = connections.get(id).id;
Net.sendExcept(player.clientid, packet, SendMode.tcp);
Net.sendExcept(id, packet, SendMode.tcp);
});
Net.handleServer(BlockTapPacket.class, (id, packet) -> {
@@ -194,8 +194,6 @@ public class NetServer extends Module{
e.type = enemy.type.id;
e.health = (short) enemy.health;
Net.sendTo(dest, e, SendMode.tcp);
} else {
Log.err("Entity request target not found!");
}
});

View File

@@ -9,6 +9,7 @@ import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.ChatPacket;
import io.anuke.mindustry.ui.fragments.DebugFragment;
import io.anuke.mindustry.world.Map;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Sounds;
@@ -119,7 +120,8 @@ public class ServerControl extends Module {
if(state.is(State.menu)){
info("&lyStatus: &rserver closed");
}else{
info("&lyStatus: &lcPlaying on map &fi{0}&fb &lb/&lc Wave {1} &lb/&lcDifficulty {2}", Strings.capitalize(world.getMap().name), state.wave, state.difficulty.name());
info("&lyStatus: &lcPlaying on map &fi{0}&fb &lb/&lc Wave {1} &lb/&lc {2}",
Strings.capitalize(world.getMap().name), state.wave, Strings.capitalize(state.difficulty.name()));
if(playerGroup.size() > 0) {
info("&lyPlayers: {0}", playerGroup.size());
for (Player p : playerGroup.all()) {
@@ -138,7 +140,7 @@ public class ServerControl extends Module {
}
netCommon.sendMessage("[pink][[Server]:[] " + arg[0]);
info("&ly[Server]: &lb{0}", arg[0]);
info("&lyServer: &lb{0}", arg[0]);
}).mergeArgs();
handler.register("difficulty", "<difficulty>", "Set game difficulty.", arg -> {
@@ -188,6 +190,10 @@ public class ServerControl extends Module {
info("Saved to slot {0}.", slot);
});
handler.register("info", "", "Print debug info", arg -> {
info(DebugFragment.debugInfo());
});
}
private void readCommands(){

View File

@@ -55,6 +55,7 @@ public class UI extends SceneModule{
public final BackgroundFragment backfrag = new BackgroundFragment();
public final LoadingFragment loadfrag = new LoadingFragment();
public final BlockConfigFragment configfrag = new BlockConfigFragment();
public final DebugFragment debugfrag = new DebugFragment();
private Locale lastLocale;
@@ -160,6 +161,7 @@ public class UI extends SceneModule{
toolfrag.build();
chatfrag.build();
listfrag.build();
debugfrag.build();
loadfrag.build();
build.end();

View File

@@ -139,14 +139,14 @@ public class EnemyType {
}
public void move(Enemy enemy){
float speed = this.speed + 0.04f * enemy.tier;
float range = this.range + enemy.tier * 5;
if(Net.client() && Net.active()){
if(Net.client()){
enemy.interpolate(); //TODO? better structure for interpolation
return;
}
float speed = this.speed + 0.04f * enemy.tier;
float range = this.range + enemy.tier * 5;
Tile core = world.getCore();
if(enemy.idletime > maxIdleLife){
@@ -194,8 +194,12 @@ public class EnemyType {
enemy.move(vec.x * Timers.delta(), vec.y * Timers.delta());
updateTargeting(enemy, nearCore);
behavior(enemy);
}
public void behavior(Enemy enemy){}
public void updateTargeting(Enemy enemy, boolean nearCore){
if(enemy.target != null && enemy.target instanceof TileEntity && ((TileEntity)enemy.target).dead){
enemy.target = null;

View File

@@ -1,4 +0,0 @@
package io.anuke.mindustry.entities.enemies;
public class StandardEnemy {
}

View File

@@ -1,7 +1,6 @@
package io.anuke.mindustry.entities.enemies.types;
import com.badlogic.gdx.math.Vector2;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.TileEntity;
@@ -9,6 +8,8 @@ import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyType;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.tilesize;
public class BlastType extends EnemyType {
public BlastType() {
@@ -22,8 +23,7 @@ public class BlastType extends EnemyType {
}
@Override
public void move(Enemy enemy){
super.move(enemy);
public void behavior(Enemy enemy){
float range = 10f;
Vector2 offset = Tmp.v3.setZero();

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.entities.enemies.types;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyType;
@@ -10,6 +9,8 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Angles;
import static io.anuke.mindustry.Vars.world;
public class FortressType extends EnemyType {
final int maxSpawn = 6;
final float spawnTime = 190;
@@ -29,7 +30,7 @@ public class FortressType extends EnemyType {
}
@Override
public void move(Enemy enemy){
public void behavior(Enemy enemy){
if(enemy.distanceTo(world.getCore().worldx(),
world.getCore().worldy()) <= 90f){
@@ -47,8 +48,6 @@ public class FortressType extends EnemyType {
enemy.spawned ++;
}
}else {
super.move(enemy);
}
}

View File

@@ -35,8 +35,7 @@ public class HealerType extends EnemyType {
}
@Override
public void move(Enemy enemy){
super.move(enemy);
public void behavior(Enemy enemy){
if(enemy.idletime > 60f*3){ //explode after 3 seconds of stillness
explode(enemy);

View File

@@ -14,11 +14,11 @@ import io.anuke.mindustry.world.blocks.types.production.Smelter;
import io.anuke.ucore.util.Bundles;
public enum Difficulty {
easy(4f, 2f, new DestrutiveHeuristic(b -> b instanceof Generator)),
normal(2f, 1f, new DestrutiveHeuristic(b -> b instanceof Smelter || b instanceof Generator)),
hard(1.5f, 0.5f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Smelter)),
insane(0.5f, 0.25f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Smelter || b instanceof Router)),
purge(0.25f, 0.01f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Router
easy(4f, 2f, 1f, new DestrutiveHeuristic(b -> b instanceof Generator)),
normal(2f, 1f, 1f, new DestrutiveHeuristic(b -> b instanceof Smelter || b instanceof Generator)),
hard(1.5f, 0.5f, 0.75f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Smelter)),
insane(0.5f, 0.25f, 0.5f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Smelter || b instanceof Router)),
purge(0.25f, 0.01f, 0.25f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Router
|| b instanceof Smelter || b instanceof Conveyor || b instanceof LiquidBlock || b instanceof PowerBlock));
/**The scaling of how many waves it takes for one more enemy of a type to appear.
@@ -27,13 +27,16 @@ public enum Difficulty {
public final float enemyScaling;
/**Multiplier of the time between waves.*/
public final float timeScaling;
/**Scaling of max time between waves. Default time is 4 minutes.*/
public final float maxTimeScaling;
/**Pathfdining heuristic for calculating tile costs.*/
public final Heuristic<Tile> heuristic;
Difficulty(float enemyScaling, float timeScaling, Heuristic<Tile> heuristic){
Difficulty(float enemyScaling, float timeScaling, float maxTimeScaling, Heuristic<Tile> heuristic){
this.enemyScaling = enemyScaling;
this.timeScaling = timeScaling;
this.heuristic = heuristic;
this.maxTimeScaling = maxTimeScaling;
}
@Override

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.game;
package io.anuke.mindustry.input;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
@@ -25,6 +25,7 @@ public class DefaultKeybinds {
"rotate", new Axis(Input.SCROLL),
"player_list", Input.TAB,
"chat", Input.ENTER,
"console", Input.GRAVE,
"weapon_1", Input.NUM_1,
"weapon_2", Input.NUM_2,
"weapon_3", Input.NUM_3,
@@ -51,6 +52,7 @@ public class DefaultKeybinds {
"rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B),
"player_list", Input.CONTROLLER_START,
"chat", Input.ENTER,
"console", Input.GRAVE,
"weapon_1", Input.NUM_1,
"weapon_2", Input.NUM_2,
"weapon_3", Input.NUM_3,

View File

@@ -0,0 +1,51 @@
package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.OrderedMap;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.reflect.ClassReflection;
public class ClientDebug {
private OrderedMap<Class<?>, Long> last = new OrderedMap<>();
private int syncPlayers = 0;
private int syncEnemies = 0;
public void handle(Object packet){
last.put(packet.getClass(), TimeUtils.millis());
}
public void setSyncDebug(int players, int enemies){
this.syncEnemies = enemies;
this.syncPlayers = players;
}
public String getOut(){
StringBuilder build = new StringBuilder();
for(Class<?> type : last.orderedKeys()){
build.append(elapsed(type));
build.append("\n");
}
build.append("sync.players: ");
build.append(syncPlayers);
build.append("\n");
build.append("sync.enemies: ");
build.append(syncEnemies);
build.append("\n");
return build.toString();
}
private String elapsed(Class<?> type){
long t = last.get(type, -1L);
if(t == -1){
return ClassReflection.getSimpleName(type) + ": <never>";
}else{
float el = TimeUtils.timeSinceMillis(t) / 1000f;
String tu;
if(el > 1f){
tu = (int)el + "s";
}else{
tu = (int)(el * 60) + "f";
}
return ClassReflection.getSimpleName(type) + ": " + tu;
}
}
}

View File

@@ -16,11 +16,10 @@ import io.anuke.ucore.util.Log;
import java.io.IOException;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.*;
public class Net{
public static final int version = 13;
public static final int version = 14;
private static boolean server;
private static boolean active;
@@ -141,6 +140,8 @@ public class Net{
/**Call to handle a packet being recieved for the client.*/
public static void handleClientReceived(Object object){
if(debugNet) clientDebug.handle(object);
if(object instanceof StreamBegin) {
StreamBegin b = (StreamBegin) object;
streams.put(b.id, new StreamBuilder(b));
@@ -170,6 +171,8 @@ public class Net{
/**Call to handle a packet being recieved for the server.*/
public static void handleServerReceived(int connection, Object object){
if(debugNet) serverDebug.handle(object);
if(serverListeners.get(object.getClass()) != null || listeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object);
if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object);

View File

@@ -0,0 +1,8 @@
package io.anuke.mindustry.net;
public class ServerDebug {
public void handle(Object packet){
}
}

View File

@@ -0,0 +1,120 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyTypes;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.scene.builders.button;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Log.LogHandler;
import static io.anuke.mindustry.Vars.*;
public class DebugFragment implements Fragment {
private static StringBuilder log = new StringBuilder();
static{
Log.setLogger(new LogHandler(){
@Override
public void print(String text, Object... args){
super.print(text, args);
log.append(Log.format(text, args));
log.append("\n");
}
});
}
@Override
public void build(){
new table(){{
visible(() -> debug);
atop().aright();
new table("pane"){{
defaults().fillX();
new label("Debug");
row();
new button("noclip", "toggle", () -> noclip = !noclip);
row();
new button("paths", "toggle", () -> showPaths = !showPaths);
row();
new button("infammo", "toggle", () -> infiniteAmmo = !infiniteAmmo);
row();
new button("wave", () -> logic.runWave());
row();
new button("clear", () -> {
enemyGroup.clear();
state.enemies = 0;
netClient.clearRecieved();
});
row();
new button("spawn", () -> new Enemy(EnemyTypes.standard).set(player.x, player.y).add());
row();
}}.end();
row();
}}.end();
new table(){{
visible(() -> console);
atop().aleft();
new table("pane") {{
defaults().fillX();
new label(DebugFragment::debugInfo);
row();
new button("dump", () -> {
try{
Gdx.files.local("packet-dump.txt").writeString(debugInfo(), false);
}catch (Exception e){
ui.showError("Error dumping log.");
}
});
}}.end();
}}.end();
new table(){{
visible(() -> console);
atop();
Table table = new Table("pane");
table.label(() -> log.toString());
ScrollPane pane = new ScrollPane(table, "clear");
get().add(pane);
}}.end();
}
public static String debugInfo(){
return join(
"net.active: " + Net.active(),
"net.server: " + Net.server(),
"players: " + playerGroup.size(),
"enemies: " + enemyGroup.size(),
"tiles: " + tileGroup.size(),
"",
clientDebug.getOut()
);
}
private static String join(String... strings){
StringBuilder builder = new StringBuilder();
for (String string : strings) {
builder.append(string);
builder.append("\n");
}
return builder.toString();
}
}

View File

@@ -7,7 +7,6 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label;
@@ -148,26 +147,6 @@ public class HudFragment implements Fragment{
}}.end();
}}.end();
//profiling table
if(debug){
new table(){{
atop();
new label("[green]density: " + Gdx.graphics.getDensity()).left();
row();
new label(() -> "[blue]requests: " + renderer.getBlocks().getRequests()).left();
row();
new label(() -> "[purple]tiles: " + tileGroup.size()).left();
row();
new label(() -> "[purple]enemies: " + enemyGroup.size()).left();
row();
new label(() -> "[orange]noclip: " + noclip).left();
row();
new label(() -> "[purple]time: " + (int)(Timers.time() / 10f) % 50).left();
row();
new label("[red]DEBUG MODE").scale(0.5f).left();
}}.end();
}
new table(){{
abottom();
visible(() -> !state.is(State.menu) && control.getSaves().isSaving());
@@ -176,14 +155,6 @@ public class HudFragment implements Fragment{
}}.end();
if(debugNet) {
new table() {{
new label(() -> "players: " + playerGroup.size());
row();
new label(() -> "" + playerGroup.all());
}}.end();
}
blockfrag.build();
}

View File

@@ -75,9 +75,13 @@ public class Door extends Wall{
return true;
}
}
if(Tmp.r2.overlaps(player.hitbox.getRect(player.x, player.y))){
return true;
for(SolidEntity e : Entities.getNearby(playerGroup, x * tilesize, y * tilesize, tilesize * 2f)){
Rectangle rect = e.hitbox.getRect(e.x, e.y);
if(Tmp.r2.overlaps(rect)){
return true;
}
}
return false;

View File

@@ -13,6 +13,7 @@ import io.anuke.ucore.entities.BulletEntity;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.renderer;
public class ShieldBlock extends PowerBlock{
@@ -76,7 +77,7 @@ public class ShieldBlock extends PowerBlock{
bullet.remove();
Effects.effect(bullet.damage > 5 ? Fx.shieldhit : Fx.laserhit, bullet);
renderer.addShieldHit(bullet.x, bullet.y);
if(!headless) renderer.addShieldHit(bullet.x, bullet.y);
entity.power -= bullet.getDamage() * powerPerDamage;
}

View File

@@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.world.Layer;
@@ -20,7 +19,7 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.*;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.*;
public class Generator extends PowerBlock{
public static final int powerTime = 2;
@@ -177,7 +176,8 @@ public class Generator extends PowerBlock{
if(target != null){
boolean interfering = isInterfering(target, rotation);
Tmp.v1.set(Angles.translation(rotation * 90, target.block().width * tilesize / 2 + 2f + (interfering ? Vector2.dst(tile.worldx(), tile.worldy(), target.worldx(), target.worldy()) / 2f - tilesize / 2f * target.block().width - 1 : 0)));
Tmp.v1.set(Angles.translation(rotation * 90, target.block().width * tilesize / 2 + 2f +
(interfering ? Vector2.dst(tile.worldx(), tile.worldy(), target.worldx(), target.worldy()) / 2f - tilesize / 2f * target.block().width + 1 : 0)));
Angles.translation(rotation * 90, width * tilesize / 2 + 2f);
@@ -191,7 +191,7 @@ public class Generator extends PowerBlock{
}
}
float r = interfering ? 0.8f : 0f;
float r = interfering ? 0f : 0f;
int relative = tile.relativeTo(target.x, target.y);