Netcode changes, new pathfinding, fixed enemies jittering when stuck

This commit is contained in:
Anuken 2018-01-12 14:01:57 -05:00
parent 1762af5d4c
commit 597a883275
23 changed files with 219 additions and 215 deletions

View File

@ -21,7 +21,7 @@ allprojects {
appName = "Mindustry"
gdxVersion = '1.9.8'
aiVersion = '1.8.1'
uCoreVersion = '9d4c546';
uCoreVersion = '459e8ae';
}
repositories {

View File

@ -45,10 +45,17 @@ public class Mindustry extends ModuleCore {
I18NBundle.setExceptionOnMissingKey(false);
if(externalBundle){
FileHandle handle = Gdx.files.local("bundle");
try {
FileHandle handle = Gdx.files.local("bundle");
Locale locale = Locale.ENGLISH;
Core.bundle = I18NBundle.createBundle(handle, locale);
Locale locale = Locale.ENGLISH;
Core.bundle = I18NBundle.createBundle(handle, locale);
}catch (Exception e){
e.printStackTrace();
platforms.showError("Failed to find bundle!\nMake sure you have bundle.properties in the same directory\nas the jar file.\n\nIf the problem persists, try running it through the command prompt:\n" +
"Hold left-shift, then right click and select 'open command prompt here'.\nThen, type in 'java -jar mindustry.jar' without quotes.");
Gdx.app.exit();
}
}else{
FileHandle handle = Gdx.files.internal("bundles/bundle");

View File

@ -1,36 +0,0 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.Heuristic;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.world.Tile;
public class HeuristicImpl implements Heuristic<Tile>{
/**How many times more it costs to go through a destructible block than an empty block.*/
static final float solidMultiplier = 5f;
/**How many times more it costs to go through a tile that touches a solid block.*/
static final float occludedMultiplier = 5f;
@Override
public float estimate(Tile node, Tile other){
return estimateStatic(node, other);
}
/**Estimate the cost of walking between two tiles.*/
public static float estimateStatic(Tile node, Tile other){
//Get Manhattan distance cost
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
//If either one of the tiles is a breakable solid block (that is, it's player-made),
//increase the cost by the tilesize times the solid block multiplier
//Also add the block health, so blocks with more health cost more to traverse
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
return cost;
}
}

View File

@ -0,0 +1,65 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.Heuristic;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.defense.Turret;
import io.anuke.mindustry.world.blocks.types.production.Drill;
import io.anuke.mindustry.world.blocks.types.production.Generator;
import io.anuke.mindustry.world.blocks.types.production.Pump;
import io.anuke.mindustry.world.blocks.types.production.Smelter;
public class Heuristics {
/**How many times more it costs to go through a destructible block than an empty block.*/
static final float solidMultiplier = 5f;
/**How many times more it costs to go through a tile that touches a solid block.*/
static final float occludedMultiplier = 5f;
public static class FastestHeuristic implements Heuristic<Tile> {
@Override
public float estimate(Tile node, Tile other){
//Get Manhattan distance cost
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
//If either one of the tiles is a breakable solid block (that is, it's player-made),
//increase the cost by the tilesize times the solid block multiplier
//Also add the block health, so blocks with more health cost more to traverse
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
return cost;
}
}
public static class DestrutiveHeuristic implements Heuristic<Tile> {
@Override
public float estimate(Tile node, Tile other){
//Get Manhattan distance cost
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
//If either one of the tiles is a breakable solid block (that is, it's player-made),
//increase the cost by the tilesize times the solid block multiplier
//Also add the block health, so blocks with more health cost more to traverse
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
//generators are free!
if(generator(other) || generator(node)) cost = 0;
return cost;
}
private boolean generator(Tile tile){
return tile.block() instanceof Generator || tile.block() instanceof Turret
|| tile.block() instanceof Pump || tile.block() instanceof Drill || tile.block() instanceof Smelter;
}
}
}

View File

@ -1,12 +1,13 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.Heuristic;
import com.badlogic.gdx.ai.pfa.PathFinderRequest;
import com.badlogic.gdx.ai.pfa.PathSmoother;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyType;
import io.anuke.mindustry.game.SpawnPoint;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.UCore;
@ -20,7 +21,7 @@ public class Pathfind{
private static final long maxTime = 1000000 * 5;
/**Heuristic for determining cost between two tiles*/
HeuristicImpl heuristic = new HeuristicImpl();
Heuristic<Tile> heuristic = new DestrutiveHeuristic();
/**Tile graph, for determining conenctions between two tiles*/
TileGraph graph = new TileGraph();
/**Smoother that removes extra nodes from a path.*/
@ -54,17 +55,17 @@ public class Pathfind{
}
//if an enemy is idle for a while, it's probably stuck
if(enemy.idletime > EnemyType.maxIdle){
//if(enemy.idletime > EnemyType.maxIdle){
Tile target = path[enemy.node];
if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
if (enemy.node > 1)
enemy.node = enemy.node - 1;
enemy.idletime = 0;
}
//Tile target = path[enemy.node];
//if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
// if (enemy.node > 1)
// enemy.node = enemy.node - 1;
// enemy.idletime = 0;
//}
//else, must be blocked by a playermade block, do nothing
}
//}
//-1 is only possible here if both pathfindings failed, which should NOT happen
//check graph code
@ -83,6 +84,12 @@ public class Pathfind{
Tile target = path[enemy.node];
//a bridge has broken
if(!Vars.world.passable(target.x, target.y)){
remakePath();
return vector.set(enemy.x, enemy.y);
}
float projectLen = Vector2.dst(prev.worldx(), prev.worldy(), target.worldx(), target.worldy()) / 6f;
Vector2 projection = projectPoint(prev.worldx(), prev.worldy(),
@ -124,6 +131,15 @@ public class Pathfind{
}
private void remakePath(){
for(int i = 0; i < Vars.control.enemyGroup.amount(); i ++){
Enemy enemy = Vars.control.enemyGroup.all().get(i);
enemy.node = -1;
}
resetPaths();
}
/**Update the pathfinders and continue calculating the path if it hasn't been calculated yet.
* This method is run each frame.*/
public void update(){

View File

@ -1,29 +0,0 @@
package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.Connection;
import io.anuke.mindustry.world.Tile;
public class TileConnection implements Connection<Tile>{
Tile a, b;
public TileConnection(Tile a, Tile b){
this.a = a;
this.b = b;
}
@Override
public float getCost(){
return HeuristicImpl.estimateStatic(a, b);
}
@Override
public Tile getFromNode(){
return a;
}
@Override
public Tile getToNode(){
return b;
}
}

View File

@ -7,23 +7,10 @@ import io.anuke.mindustry.world.Tile;
/**Tilegraph that ignores player-made tiles.*/
public class TileGraph implements OptimizedGraph<Tile> {
private Array<Connection<Tile>> tempConnections = new Array<Connection<Tile>>(4);
/**Used for the default Graph implementation. Returns a result similar to connectionsOf()*/
/**return nothing, as this isn't used*/
@Override
public Array<Connection<Tile>> getConnections(Tile fromNode){
tempConnections.clear();
if(!fromNode.passable())
return tempConnections;
for(Tile tile : fromNode.getNearby()){
if(tile != null && (tile.passable()))
tempConnections.add(new TileConnection(fromNode, tile));
}
return tempConnections;
}
public Array<Connection<Tile>> getConnections(Tile fromNode){ return null; }
/**Used for the OptimizedPathFinder implementation.*/
@Override

View File

@ -8,14 +8,13 @@ import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.Bullet;
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.entities.enemies.EnemyTypes;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.EnemySpawn;
import io.anuke.mindustry.game.SpawnPoint;
import io.anuke.mindustry.game.WaveCreator;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.input.AndroidInput;
import io.anuke.mindustry.input.DesktopInput;
@ -25,7 +24,9 @@ import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.ItemStack;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Map;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.ProductionBlocks;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.*;
@ -63,7 +64,7 @@ public class Control extends Module{
float wavetime;
float extrawavetime;
int enemies = 0;
io.anuke.mindustry.game.GameMode mode = io.anuke.mindustry.game.GameMode.waves;
GameMode mode = GameMode.waves;
Tile core;
Array<io.anuke.mindustry.game.SpawnPoint> spawnpoints = new Array<>();
@ -180,6 +181,7 @@ public class Control extends Module{
"rotate_alt", new Axis(Input.CONTROLLER_DPAD_RIGHT, Input.CONTROLLER_DPAD_LEFT),
"rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B),
"player_list", Input.CONTROLLER_START,
"chat", Input.ENTER,
"weapon_1", Input.NUM_1,
"weapon_2", Input.NUM_2,
"weapon_3", Input.NUM_3,
@ -279,7 +281,7 @@ public class Control extends Module{
return core;
}
public Array<io.anuke.mindustry.game.SpawnPoint> getSpawnPoints(){
public Array<SpawnPoint> getSpawnPoints(){
return spawnpoints;
}
@ -292,7 +294,7 @@ public class Control extends Module{
}
public void addSpawnPoint(Tile tile){
io.anuke.mindustry.game.SpawnPoint point = new SpawnPoint();
SpawnPoint point = new SpawnPoint();
point.start = tile;
spawnpoints.add(point);
}
@ -310,11 +312,11 @@ public class Control extends Module{
Timers.run(18, ()-> ui.loadfrag.hide());
}
public io.anuke.mindustry.game.GameMode getMode(){
public GameMode getMode(){
return mode;
}
public void setMode(io.anuke.mindustry.game.GameMode mode){
public void setMode(GameMode mode){
this.mode = mode;
}
@ -597,7 +599,7 @@ public class Control extends Module{
}
if(Inputs.keyTap(Keys.U)){
Vars.showUI = !Vars.showUI;
Vars.showPaths = !Vars.showPaths;
}
if(Inputs.keyTap(Keys.O)){

View File

@ -26,6 +26,7 @@ import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.BaseBulletType;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.modules.Module;
@ -37,7 +38,7 @@ public class NetClient extends Module {
Color.GOLD, Color.PINK, Color.SKY, Color.GOLD, Color.VIOLET,
Color.GREEN, Color.CORAL, Color.CYAN, Color.CHARTREUSE};
boolean connecting = false;
boolean gotEntities = false, gotData = false;
boolean gotData = false;
boolean kicked = false;
IntSet requests = new IntSet();
float playerSyncTime = 2;
@ -48,22 +49,23 @@ public class NetClient extends Module {
Net.handle(Connect.class, packet -> {
requests.clear();
connecting = true;
gotEntities = false;
gotData = false;
kicked = false;
Gdx.app.postRunnable(() -> {
Vars.ui.loadfrag.hide();
Vars.ui.loadfrag.show("$text.connecting.data");
Entities.clear();
ConnectPacket c = new ConnectPacket();
c.name = Vars.player.name;
c.android = Vars.android;
Net.send(c, SendMode.tcp);
});
ConnectPacket c = new ConnectPacket();
c.name = Vars.player.name;
c.android = Vars.android;
Net.send(c, SendMode.tcp);
Timers.runTask(dataTimeout, () -> {
if(!gotEntities){
if(!gotData){
Gdx.app.error("Mindustry", "Failed to load data!");
Vars.ui.loadfrag.hide();
Net.disconnect();
@ -90,32 +92,18 @@ public class NetClient extends Module {
NetworkIO.load(data.stream);
Vars.player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2);
GameState.set(State.playing);
connecting = false;
Vars.ui.loadfrag.hide();
Vars.ui.join.hide();
gotData = true;
});
});
Net.handle(EntityDataPacket.class, data -> {
Gdx.app.postRunnable(() -> {
Timers.run(10f, () -> { //TODO hack. should only run once world data is recieved
Vars.control.playerGroup.remap(Vars.player, data.playerid);
for(int i = 0; i < data.weapons.length; i ++){
Vars.control.addWeapon((Weapon) Upgrade.getByID(data.weapons[i]));
}
Vars.player.weaponLeft = Vars.player.weaponRight = Vars.control.getWeapons().peek();
Vars.ui.hudfrag.updateWeapons();
gotEntities = true;
});
Net.send(new ConnectConfirmPacket(), SendMode.tcp);
GameState.set(State.playing);
});
});
Net.handle(SyncPacket.class, packet -> {
if(!gotEntities) return;
if(!gotData) return;
//TODO awful code
for(int i = 0; i < packet.ids.length; i ++){
@ -177,11 +165,13 @@ public class NetClient extends Module {
Gdx.app.postRunnable(() -> {
//duplicates.
if(Vars.control.enemyGroup.getByID(spawn.id) != null) return;
Enemy enemy = new Enemy(EnemyType.getByID(spawn.type));
enemy.set(spawn.x, spawn.y);
enemy.tier = spawn.tier;
enemy.lane = spawn.lane;
enemy.id = spawn.id;
enemy.health = spawn.health;
enemy.add();
Effects.effect(Fx.spawn, enemy);
@ -221,7 +211,7 @@ public class NetClient extends Module {
});
Net.handle(BlockSyncPacket.class, packet -> {
if(!gotEntities) return;
if(!gotData) return;
DataInputStream stream = new DataInputStream(packet.stream);
@ -306,7 +296,7 @@ public class NetClient extends Module {
if(!Net.client() || !Net.active()) return;
if(!GameState.is(State.menu) && Net.active()){
if(gotEntities && gotData) sync();
if(gotData) sync();
}else if(!connecting){
Net.disconnect();
}

View File

@ -43,18 +43,7 @@ public class NetServer extends Module{
UCore.log("Sending world data to client (ID="+id+")");
WorldData data = new WorldData();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
NetworkIO.write(stream);
UCore.log("Packed " + stream.size() + " uncompressed bytes of data.");
data.stream = new ByteArrayInputStream(stream.toByteArray());
Net.sendStream(id, data);
Gdx.app.postRunnable(() -> {
EntityDataPacket dp = new EntityDataPacket();
Player player = new Player();
player.clientid = id;
player.name = packet.name;
@ -62,18 +51,29 @@ public class NetServer extends Module{
player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2);
player.getInterpolator().last.set(player.x, player.y);
player.getInterpolator().target.set(player.x, player.y);
player.add();
connections.put(id, player);
dp.playerid = player.id;
dp.weapons = weapons.get(packet.name, new ByteArray()).toArray();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
NetworkIO.write(player.id, weapons.get(packet.name, new ByteArray()), stream);
Net.sendTo(id, dp, SendMode.tcp);
UCore.log("Packed " + stream.size() + " uncompressed bytes of data.");
sendMessage("[accent]"+Bundles.format("text.server.connected", packet.name));
WorldData data = new WorldData();
data.stream = new ByteArrayInputStream(stream.toByteArray());
Net.sendStream(id, data);
});
});
Net.handleServer(ConnectConfirmPacket.class, packet -> {
Player player = connections.get(Net.getLastConnection());
if(player == null) return;
Gdx.app.postRunnable(player::add);
sendMessage("[accent]"+Bundles.format("text.server.connected", player.name));
});
Net.handleServer(Disconnect.class, packet -> {
Player player = connections.get(packet.id);
@ -197,6 +197,7 @@ public class NetServer extends Module{
e.tier = (byte)enemy.tier;
e.lane = (byte)enemy.lane;
e.type = enemy.type.id;
e.health = (short)enemy.health;
Net.sendTo(dest, e, SendMode.tcp);
Gdx.app.error("Mindustry", "Replying to entity request("+Net.getLastConnection()+"): enemy, " + id);
}else{

View File

@ -10,7 +10,7 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.io.Maps;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Generator;
import io.anuke.mindustry.world.WorldGenerator;
import io.anuke.mindustry.world.Map;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
@ -180,7 +180,7 @@ public class World extends Module{
Entities.resizeTree(0, 0, map.getWidth() * tilesize, map.getHeight() * tilesize);
this.seed = seed;
Generator.generate(map.pixmap, tiles);
WorldGenerator.generate(map.pixmap, tiles);
if(control.getCore() == null) return;

View File

@ -123,7 +123,7 @@ public class Player extends DestructibleEntity implements Syncable{
dashing = Inputs.keyDown("dash");
float speed = dashing ? Player.dashSpeed : Player.speed;
float speed = dashing ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.speed;
if(health < maxhealth && Timers.get(this, "regen", 20))
health ++;

View File

@ -30,6 +30,7 @@ public class Enemy extends DestructibleEntity implements Syncable{
public Entity target;
public float hitTime;
public int tier = 1;
public Vector2 totalMove = new Vector2();
public Enemy(EnemyType type){
this.type = type;

View File

@ -18,6 +18,7 @@ import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.world;
@ -30,8 +31,7 @@ public class EnemyType {
public final static Color[] tierColors = { Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), Color.valueOf("ff2d86") };
public final static int maxtier = 4;
public final static float maxIdle = 60*1.5f;
public final static float maxIdleLife = 60f*13f; //13 seconds idle = death
public final static float maxIdleLife = 60f*2f; //2 seconds idle = death
public final static float hitDuration = 5f;
public final String name;
@ -56,6 +56,7 @@ public class EnemyType {
protected final int timerTarget = timeid ++;
protected final int timerReload = timeid ++;
protected final int timerReset = timeid ++;
public EnemyType(String name){
this.id = lastid++;
@ -76,6 +77,14 @@ public class EnemyType {
Draw.color();
Graphics.flush();
if(Vars.showPaths){
Draw.tscl(0.25f);
Draw.text((int)enemy.idletime + "\n" + Strings.toFixed(enemy.totalMove.x, 2) + ", "
+ Strings.toFixed(enemy.totalMove.x, 2), enemy.x, enemy.y);
Draw.tscl(Vars.fontscale);
}
Shaders.outline.lighten = 0f;
}
@ -90,15 +99,24 @@ public class EnemyType {
move(enemy);
enemy.velocity.set(enemy.x - lastx, enemy.y - lasty).scl(1f / Timers.delta());
enemy.totalMove.add(enemy.velocity);
float minv = 0.07f;
if(enemy.timer.get(timerReset, 60)){
enemy.totalMove.setZero();
}
if(enemy.velocity.len() < minv && enemy.node > 0 && enemy.target == null){
enemy.idletime += Timers.delta();
}else{
enemy.idletime = 0;
}
if(enemy.timer.getTime(timerReset) > 40 && enemy.totalMove.len() < 0.3f && enemy.node > 0 && enemy.target == null){
enemy.idletime = 999999f;
}
Tile tile = world.tileWorld(enemy.x, enemy.y);
if(tile != null && tile.floor().liquid && tile.block() == Blocks.air){
enemy.damage(enemy.health+1); //drown

View File

@ -1,15 +1,15 @@
package io.anuke.mindustry.io;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ByteArray;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyType;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.resource.Upgrade;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.WorldGenerator;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.mindustry.world.blocks.types.BlockPart;
import io.anuke.mindustry.world.blocks.types.Rock;
@ -19,9 +19,9 @@ import io.anuke.ucore.entities.Entities;
import java.io.*;
public class NetworkIO {
private static final int fileVersionID = 13;
private static final int fileVersionID = 14;
public static void write(OutputStream os){
public static void write(int playerID, ByteArray upgrades, OutputStream os){
try(DataOutputStream stream = new DataOutputStream(os)){
@ -36,28 +36,20 @@ public class NetworkIO {
stream.writeInt(Vars.control.getWave()); //wave
stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown
stream.writeInt(Vars.control.enemyGroup.amount()); //enemy amount
stream.writeInt(playerID); //player remap ID
//--INVENTORY--
for(int i = 0; i < Vars.control.getItems().length; i ++){
for(int i = 0; i < Vars.control.getItems().length; i ++){ //items
stream.writeInt(Vars.control.getItems()[i]);
}
//--ENEMIES--
Array<Enemy> enemies = Vars.control.enemyGroup.all();
stream.writeByte(upgrades.size); //upgrade data
stream.writeInt(enemies.size); //enemy amount
for(int i = 0; i < enemies.size; i ++){
Enemy enemy = enemies.get(i);
stream.writeInt(enemy.id);
stream.writeByte(enemy.type.id); //type
stream.writeByte(enemy.lane); //lane
stream.writeFloat(enemy.x); //x
stream.writeFloat(enemy.y); //y
stream.writeByte(enemy.tier); //tier
stream.writeShort(enemy.health); //health
stream.writeShort(enemy.node); //current node
for(int i = 0; i < upgrades.size; i ++){
stream.writeByte(upgrades.get(i));
}
//--MAP DATA--
@ -166,7 +158,7 @@ public class NetworkIO {
Timers.resetTime(timerTime + (TimeUtils.timeSinceMillis(timestamp) / 1000f) * 60f);
if(version != fileVersionID){
throw new RuntimeException("Save file version mismatch!");
throw new RuntimeException("Netcode version mismatch!");
}
//general state
@ -175,9 +167,13 @@ public class NetworkIO {
int wave = stream.readInt();
float wavetime = stream.readFloat();
int enemies = stream.readInt();
Vars.control.setWaveData(enemies, wave, wavetime);
Vars.control.setMode(GameMode.values()[mode]);
int pid = stream.readInt();
//inventory
for(int i = 0; i < Vars.control.getItems().length; i ++){
Vars.control.getItems()[i] = stream.readInt();
@ -187,38 +183,18 @@ public class NetworkIO {
Vars.control.getWeapons().clear();
Vars.control.getWeapons().add(Weapon.blaster);
Vars.player.weaponLeft = Vars.player.weaponRight = Weapon.blaster;
Vars.ui.hudfrag.updateWeapons();
//enemies
byte weapons = stream.readByte();
Entities.clear();
int enemies = stream.readInt();
for(int i = 0; i < enemies; i ++){
int id = stream.readInt();
byte type = stream.readByte();
int lane = stream.readByte();
float x = stream.readFloat();
float y = stream.readFloat();
byte tier = stream.readByte();
short health = stream.readShort();
short node = stream.readShort();
Enemy enemy = new Enemy(EnemyType.getByID(type));
enemy.id = id;
enemy.lane = lane;
enemy.health = health;
enemy.x = x;
enemy.y = y;
enemy.tier = tier;
enemy.node = node;
enemy.add(Vars.control.enemyGroup);
for(int i = 0; i < weapons; i ++){
Vars.control.getWeapons().add((Weapon) Upgrade.getByID(stream.readByte()));
}
Vars.control.setWaveData(enemies, wave, wavetime);
Vars.player.weaponLeft = Vars.player.weaponRight = Vars.control.getWeapons().peek();
Vars.ui.hudfrag.updateWeapons();
Entities.clear();
Vars.player.id = pid;
Vars.player.add();
//map
@ -246,7 +222,7 @@ public class NetworkIO {
for(int i = 0; i < rocks; i ++){
int pos = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
Block result = io.anuke.mindustry.world.Generator.rocks.get(tile.floor());
Block result = WorldGenerator.rocks.get(tile.floor());
if(result != null) tile.setBlock(result);
}

View File

@ -8,6 +8,7 @@ public abstract class PlatformFunction{
public String format(Date date){return "invalid";}
public String format(int number){return "invalid";}
public void openLink(String link){}
public void showError(String text){}
public void addDialog(TextField field){
addDialog(field, 16);
}

View File

@ -148,7 +148,7 @@ public class Save13 extends SaveFileVersion {
for(int i = 0; i < rocks; i ++){
int pos = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
Block result = Generator.rocks.get(tile.floor());
Block result = WorldGenerator.rocks.get(tile.floor());
if(result != null) tile.setBlock(result);
}

View File

@ -12,7 +12,7 @@ import io.anuke.mindustry.resource.Upgrade;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.world.Generator;
import io.anuke.mindustry.world.WorldGenerator;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.mindustry.world.blocks.types.BlockPart;
@ -165,7 +165,7 @@ public class Save14 extends SaveFileVersion{
for(int i = 0; i < rocks; i ++){
int pos = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
Block result = Generator.rocks.get(tile.floor());
Block result = WorldGenerator.rocks.get(tile.floor());
if(result != null) tile.setBlock(result);
}

View File

@ -17,11 +17,6 @@ public class Packets {
}
public static class EntityDataPacket{
public int playerid;
public byte[] weapons;
}
public static class SyncPacket{
public int[] ids;
public float[][] data;
@ -37,6 +32,10 @@ public class Packets {
public boolean android;
}
public static class ConnectConfirmPacket{
}
public static class DisconnectPacket{
public int playerid;
}
@ -85,6 +84,7 @@ public class Packets {
public static class EnemySpawnPacket{
public byte type, lane, tier;
public float x, y;
public short health;
public int id;
}

View File

@ -18,7 +18,6 @@ public class Registrator {
StreamChunk.class,
WorldData.class,
SyncPacket.class,
EntityDataPacket.class,
PositionPacket.class,
ShootPacket.class,
PlacePacket.class,
@ -39,6 +38,7 @@ public class Registrator {
BlockTapPacket.class,
BlockConfigPacket.class,
EntityRequestPacket.class,
ConnectConfirmPacket.class,
Class.class,
byte[].class,

View File

@ -16,7 +16,7 @@ import io.anuke.ucore.graphics.Hue;
import io.anuke.ucore.noise.Noise;
import io.anuke.ucore.util.Mathf;
public class Generator{
public class WorldGenerator {
public static final ObjectMap<Block, Block> rocks = new ObjectMap(){{
put(Blocks.stone, Blocks.rock);
put(Blocks.snow, Blocks.icerock);

View File

@ -19,7 +19,7 @@ public class CoreBlock extends Block {
@Override
public int handleDamage(Tile tile, int amount){
return Vars.debug ? amount : amount;
return Vars.debug ? 0 : amount;
}
@Override

View File

@ -81,6 +81,11 @@ public class DesktopLauncher {
}
}
@Override
public void showError(String text){
JOptionPane.showMessageDialog(null, text);
}
@Override
public void updateRPC() {
DiscordRichPresence presence = new DiscordRichPresence();