Compare commits

..

4 Commits
v43 ... v44

Author SHA1 Message Date
Anuken
423f5f16bb Fixed extremely glitchy player pickup 2018-07-04 12:31:09 -04:00
Anuken
1c8c702afd Made flying units appear later in waves 2018-07-04 11:58:41 -04:00
Anuken
d2a739262d Added admin wave sending / Admin system fixes / Build fix 2018-07-04 11:57:04 -04:00
Anuken
76d0285e3f Added freebuild / Power system fix / Net packet pool fix / Crash fixes 2018-07-04 11:38:59 -04:00
40 changed files with 202 additions and 156 deletions

View File

@@ -1,7 +1,6 @@
text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]
text.credits=Credits text.credits=Credits
text.discord=Join the mindustry discord! text.discord=Join the mindustry discord!
text.changes=[SCARLET]Attention!\n[]Some important game mechanics have been changed.\n\n- [accent]Teleporters[] now use power.\n- [accent]Smelteries[] and [accent]crucibles[] now have a maximum item capacity.\n- [accent]Crucibles[] now require coal as fuel.
text.link.discord.description=the official Mindustry discord chatroom text.link.discord.description=the official Mindustry discord chatroom
text.link.github.description=Game source code text.link.github.description=Game source code
text.link.dev-builds.description=Unstable development builds text.link.dev-builds.description=Unstable development builds
@@ -271,15 +270,6 @@ text.info.title=[accent]Info
text.error.title=[crimson]An error has occured text.error.title=[crimson]An error has occured
text.error.crashmessage=[SCARLET]An unexpected error has occured, which would have caused a crash.\n[]Please report the exact circumstances under which this error occured to the developer: \n[ORANGE]anukendev@gmail.com[] text.error.crashmessage=[SCARLET]An unexpected error has occured, which would have caused a crash.\n[]Please report the exact circumstances under which this error occured to the developer: \n[ORANGE]anukendev@gmail.com[]
text.error.crashtitle=An error has occured text.error.crashtitle=An error has occured
text.mode.break=Break mode: {0}
text.mode.place=Place mode: {0}
placemode.hold.name=line
placemode.areadelete.name=area
placemode.touchdelete.name=touch
placemode.holddelete.name=hold
placemode.none.name=none
placemode.touch.name=touch
placemode.cursor.name=cursor
text.blocks.blockinfo=Block Info text.blocks.blockinfo=Block Info
text.blocks.powercapacity=Power Capacity text.blocks.powercapacity=Power Capacity
text.blocks.powershot=Power/Shot text.blocks.powershot=Power/Shot

View File

@@ -13,7 +13,7 @@ public class PowerBlocks extends BlockList implements ContentList {
@Override @Override
public void load() { public void load() {
combustionGenerator = new BurnerGenerator("combustion-generator") {{ combustionGenerator = new BurnerGenerator("combustion-generator") {{
powerOutput = 0.06f; powerOutput = 0.09f;
powerCapacity = 40f; powerCapacity = 40f;
itemDuration = 40f; itemDuration = 40f;
}}; }};
@@ -27,7 +27,7 @@ public class PowerBlocks extends BlockList implements ContentList {
}}; }};
turbineGenerator = new TurbineGenerator("turbine-generator") {{ turbineGenerator = new TurbineGenerator("turbine-generator") {{
powerOutput = 0.15f; powerOutput = 0.25f;
powerCapacity = 40f; powerCapacity = 40f;
itemDuration = 30f; itemDuration = 30f;
size = 2; size = 2;

View File

@@ -1,6 +1,9 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import com.badlogic.gdx.utils.*; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectSet;
import com.badlogic.gdx.utils.OrderedMap;
import com.badlogic.gdx.utils.OrderedSet;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.content.blocks.*; import io.anuke.mindustry.content.blocks.*;
import io.anuke.mindustry.content.bullets.*; import io.anuke.mindustry.content.bullets.*;
@@ -28,6 +31,7 @@ public class ContentLoader {
private static boolean loaded = false; private static boolean loaded = false;
private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>(); private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>();
private static OrderedMap<String, Array<Content>> contentMap = new OrderedMap<>(); private static OrderedMap<String, Array<Content>> contentMap = new OrderedMap<>();
private static ObjectSet<Consumer<Content>> initialization = new ObjectSet<>();
private static ContentList[] content = { private static ContentList[] content = {
//effects //effects
new BlockFx(), new BlockFx(),
@@ -132,11 +136,15 @@ public class ContentLoader {
/**Initializes all content with the specified function.*/ /**Initializes all content with the specified function.*/
public static void initialize(Consumer<Content> callable){ public static void initialize(Consumer<Content> callable){
if(initialization.contains(callable)) return;
for(Array<? extends Content> arr : contentSet){ for(Array<? extends Content> arr : contentSet){
for(Content content : arr){ for(Content content : arr){
callable.accept(content); callable.accept(content);
} }
} }
initialization.add(callable);
} }
public static void dispose(){ public static void dispose(){

View File

@@ -13,10 +13,10 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.ContentDatabase; import io.anuke.mindustry.game.ContentDatabase;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.input.MobileInput;
import io.anuke.mindustry.input.DefaultKeybinds; import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.input.DesktopInput; import io.anuke.mindustry.input.DesktopInput;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.input.MobileInput;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.Saves; import io.anuke.mindustry.io.Saves;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
@@ -285,6 +285,7 @@ public class Control extends Module{
@Override @Override
public void dispose(){ public void dispose(){
Platform.instance.onGameExit(); Platform.instance.onGameExit();
ContentLoader.dispose();
Net.dispose(); Net.dispose();
ui.editor.dispose(); ui.editor.dispose();
} }
@@ -321,6 +322,18 @@ public class Control extends Module{
}); });
} }
if(!Settings.has("4.0-no-sound")){
Settings.putBool("4.0-no-sound", true);
Timers.runTask(4f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]Attention![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("You might have noticed that 4.0 does not have any sound.\nThis is [orange]intentional![] Sound will be added in a later update.\n\n[LIGHT_GRAY](now stop reporting this as a bug)").wrap().width(500f);
dialog.show();
});
}
} }
/**Called from main logic thread.*/ /**Called from main logic thread.*/
@@ -355,7 +368,7 @@ public class Control extends Module{
} }
//check unlocks every 2 seconds //check unlocks every 2 seconds
if(!state.mode.infiniteResources && !state.mode.disableWaveTimer && Timers.get("timerCheckUnlock", 120)){ if(!state.mode.infiniteResources && Timers.get("timerCheckUnlock", 120)){
checkUnlockableBlocks(); checkUnlockableBlocks();
//save if the db changed, but don't save unlocks //save if the db changed, but don't save unlocks

View File

@@ -12,8 +12,6 @@ public class GameState{
public int wave = 1; public int wave = 1;
public float wavetime; public float wavetime;
public float extrawavetime;
public int enemies = 0;
public boolean gameOver = false; public boolean gameOver = false;
public GameMode mode = GameMode.waves; public GameMode mode = GameMode.waves;
public Difficulty difficulty = Difficulty.normal; public Difficulty difficulty = Difficulty.normal;

View File

@@ -70,9 +70,7 @@ public class Logic extends Module {
public void reset(){ public void reset(){
state.wave = 1; state.wave = 1;
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
state.wavetime = wavespace * state.difficulty.timeScaling; state.wavetime = wavespace * state.difficulty.timeScaling;
state.enemies = 0;
state.gameOver = false; state.gameOver = false;
state.teams = new TeamInfo(); state.teams = new TeamInfo();
state.teams.add(Team.blue, true); state.teams.add(Team.blue, true);
@@ -89,7 +87,6 @@ public class Logic extends Module {
state.spawner.spawnEnemies(); state.spawner.spawnEnemies();
state.wave ++; state.wave ++;
state.wavetime = wavespace * state.difficulty.timeScaling; state.wavetime = wavespace * state.difficulty.timeScaling;
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
Events.fire(WaveEvent.class); Events.fire(WaveEvent.class);
} }
@@ -133,15 +130,10 @@ public class Logic extends Module {
if(!state.is(State.paused) || Net.active()){ if(!state.is(State.paused) || Net.active()){
if(!state.mode.disableWaveTimer){ if(!state.mode.disableWaveTimer){
state.wavetime -= Timers.delta();
if(state.enemies <= 0){
if(!world.getMap().name.equals("tutorial")) state.wavetime -= Timers.delta();
}else{
state.extrawavetime -= Timers.delta();
}
} }
if(!Net.client() && (state.wavetime <= 0 || state.extrawavetime <= 0)){ if(!Net.client() && state.wavetime <= 0){
runWave(); runWave();
} }

View File

@@ -219,6 +219,7 @@ public class NetClient extends Module {
new Random().nextBytes(bytes); new Random().nextBytes(bytes);
String result = new String(Base64Coder.encode(bytes)); String result = new String(Base64Coder.encode(bytes));
Settings.putString("usid-" + ip, result); Settings.putString("usid-" + ip, result);
Settings.save();
return result; return result;
} }
} }

View File

@@ -175,7 +175,7 @@ public class NetServer extends Module{
NetConnection connection = Net.getConnection(id); NetConnection connection = Net.getConnection(id);
if(player == null || connection == null || packet.snapid < connection.lastRecievedClientSnapshot) return; if(player == null || connection == null || packet.snapid < connection.lastRecievedClientSnapshot) return;
boolean verifyPosition = !player.isDead() && !debug && headless && !player.mech.flying; boolean verifyPosition = !player.isDead() && !debug && headless && !player.mech.flying && player.getCarrier() == null;
if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = TimeUtils.millis() - 16; if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = TimeUtils.millis() - 16;
@@ -485,14 +485,18 @@ public class NetServer extends Module{
return; return;
} }
if(other == null || other.isAdmin){ if(other == null || (other.isAdmin && other != player)){ //fun fact: this means you can ban yourself
Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name); Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
return; return;
} }
String ip = player.con.address; String ip = player.con.address;
if(action == AdminAction.ban){ if(action == AdminAction.wave) {
//no verification is done, so admins can hypothetically spam waves
//not a real issue, because server owners may want to do just that
state.wavetime = 0f;
}else if(action == AdminAction.ban){
netServer.admins.banPlayerIP(ip); netServer.admins.banPlayerIP(ip);
netServer.kick(other.con.id, KickReason.banned); netServer.kick(other.con.id, KickReason.banned);
Log.info("&lc{0} has banned {1}.", player.name, other.name); Log.info("&lc{0} has banned {1}.", player.name, other.name);

View File

@@ -306,12 +306,19 @@ public class Renderer extends RendererModule{
} }
} }
//TODO extremely hacky
if(t instanceof Player && ((Player) t).getCarry() != null && ((Player) t).getCarry() instanceof Player && ((Player) ((Player) t).getCarry()).isLocal){
((Player) t).x = ((Player) t).getCarry().getX();
((Player) t).y = ((Player) t).getCarry().getY();
}
drawer.accept(t); drawer.accept(t);
if(threads.doInterpolate() && threads.isEnabled()) {
t.setX(lastx); t.setX(lastx);
t.setY(lasty); t.setY(lasty);
if(threads.doInterpolate() && threads.isEnabled()) {
if (t instanceof SolidTrait) { if (t instanceof SolidTrait) {
((SolidTrait) t).setRotation(lastrot); ((SolidTrait) t).setRotation(lastrot);
} }

View File

@@ -92,8 +92,6 @@ public class UI extends SceneModule{
Settings.setErrorHandler(()-> Timers.run(1f, ()-> showError("[crimson]Failed to access local storage.\nSettings will not be saved."))); Settings.setErrorHandler(()-> Timers.run(1f, ()-> showError("[crimson]Failed to access local storage.\nSettings will not be saved.")));
Settings.defaults("pixelate", true);
Dialog.closePadR = -1; Dialog.closePadR = -1;
Dialog.closePadT = 5; Dialog.closePadT = 5;

View File

@@ -268,6 +268,11 @@ public class MapEditor{
public void resize(int width, int height){ public void resize(int width, int height){
map = new MapTileData(width, height); map = new MapTileData(width, height);
for (int x = 0; x < map.width(); x++) {
for (int y = 0; y < map.height(); y++) {
map.write(x, y, DataPosition.floor, (byte)Blocks.stone.id);
}
}
renderer.resize(width, height); renderer.resize(width, height);
} }
} }

View File

@@ -369,8 +369,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
public void updateSelectedBlock(){ public void updateSelectedBlock(){
Block block = editor.getDrawBlock(); Block block = editor.getDrawBlock();
int i = 0; int i = 0;
for(Block test : Block.all()){ for(int j = 0; j < Block.all().size; j ++){
if(block == test){ if(block.id == j){
blockgroup.getButtons().get(i).setChecked(true); blockgroup.getButtons().get(i).setChecked(true);
break; break;
} }

View File

@@ -61,7 +61,7 @@ public class MapRenderer implements Disposable{
while(it.hasNext){ while(it.hasNext){
int i = it.next(); int i = it.next();
int x = i % width; int x = i % width;
int y = i / height; int y = i / width;
render(x, y); render(x, y);
} }
updates.clear(); updates.clear();

View File

@@ -30,10 +30,7 @@ import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.ThreadQueue;
import io.anuke.ucore.util.Timer;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -451,6 +448,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
updateBuilding(this); //building happens even with non-locals updateBuilding(this); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes status.update(this); //status effect updating also happens with non locals for effect purposes
if(getCarrier() != null){
x = getCarrier().getX();
y = getCarrier().getY();
}
if(Net.server()){ if(Net.server()){
updateShooting(); //server simulates player shooting updateShooting(); //server simulates player shooting
} }
@@ -532,11 +534,16 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
movement.limit(speed); movement.limit(speed);
if(getCarrier() == null){
velocity.add(movement); velocity.add(movement);
float prex = x, prey = y; float prex = x, prey = y;
updateVelocityStatus(mech.drag, 10f); updateVelocityStatus(mech.drag, 10f);
moved = distanceTo(prex, prey) > 0.01f; moved = distanceTo(prex, prey) > 0.01f;
}else{
velocity.setZero();
x = Mathf.lerpDelta(x, getCarrier().getX(), 0.1f);
y = Mathf.lerpDelta(y, getCarrier().getY(), 0.1f);
}
if(!isShooting()){ if(!isShooting()){
if(!movement.isZero()) { if(!movement.isZero()) {
@@ -610,7 +617,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if (target == null) { if (target == null) {
isShooting = false; isShooting = false;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else { } else if(target.isValid()){
//rotate toward and shoot the target //rotate toward and shoot the target
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f); rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
@@ -697,12 +704,18 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public void readSave(DataInput stream) throws IOException { public void readSave(DataInput stream) throws IOException {
boolean local = stream.readBoolean(); boolean local = stream.readBoolean();
if(local){ if(local && !headless){
byte mechid = stream.readByte(); byte mechid = stream.readByte();
int index = stream.readByte(); int index = stream.readByte();
players[index].readSaveSuper(stream); players[index].readSaveSuper(stream);
players[index].mech = Upgrade.getByID(mechid); players[index].mech = Upgrade.getByID(mechid);
players[index].dead = false; players[index].dead = false;
}else if(local){
byte mechid = stream.readByte();
stream.readByte();
readSaveSuper(stream);
mech = Upgrade.getByID(mechid);
dead = false;
} }
} }

View File

@@ -196,7 +196,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void avoidOthers(float avoidRange){ public void avoidOthers(float avoidRange){
EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> { EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> {
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying())))) return; if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t)) return;
float dst = distanceTo(t); float dst = distanceTo(t);
if(dst > avoidRange) return; if(dst > avoidRange) return;
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange)))); velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange))));

View File

@@ -43,7 +43,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/ /**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/
public static void create(Tile tile){ public static void create(Tile tile){
if(Net.client()) return; //not clientside. if(Net.client() || tile == null) return; //not clientside.
Fire fire = map.get(tile.packedPosition()); Fire fire = map.get(tile.packedPosition());
@@ -62,7 +62,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/ /**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/
public static void extinguish(Tile tile, float intensity) { public static void extinguish(Tile tile, float intensity) {
if (map.containsKey(tile.packedPosition())) { if (tile != null && map.containsKey(tile.packedPosition())) {
map.get(tile.packedPosition()).time += intensity * Timers.delta(); map.get(tile.packedPosition()).time += intensity * Timers.delta();
} }
} }

View File

@@ -69,6 +69,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
Effects.effect(UnitFx.pickup, drop); Effects.effect(UnitFx.pickup, drop);
} }
itemGroup.removeByID(itemid); itemGroup.removeByID(itemid);
netClient.addRemovedEntity(itemid);
} }
/**Internal use only!*/ /**Internal use only!*/

View File

@@ -4,12 +4,13 @@ import io.anuke.ucore.util.Bundles;
public enum GameMode{ public enum GameMode{
waves, waves,
sandbox{ //disabled for technical reasons
/*sandbox{
{ {
infiniteResources = true; infiniteResources = true;
disableWaveTimer = true; disableWaveTimer = true;
} }
}, },*/
freebuild{ freebuild{
{ {
disableWaveTimer = true; disableWaveTimer = true;

View File

@@ -17,8 +17,8 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.vtol){{ new SpawnGroup(UnitTypes.vtol){{
begin = 4; begin = 6;
end = 6; end = 8;
}}, }},
new SpawnGroup(UnitTypes.scout){{ new SpawnGroup(UnitTypes.scout){{
@@ -29,7 +29,7 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.titan){{ new SpawnGroup(UnitTypes.titan){{
begin = 8; begin = 9;
spacing = 3; spacing = 3;
unitScaling = 2; unitScaling = 2;
@@ -71,7 +71,7 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.vtol){{ new SpawnGroup(UnitTypes.vtol){{
begin = 14; begin = 16;
unitScaling = 2; unitScaling = 2;
spacing = 2; spacing = 2;
@@ -120,9 +120,10 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.monsoon){{ new SpawnGroup(UnitTypes.monsoon){{
begin = 35; begin = 40;
ammoItem = Items.blastCompound; ammoItem = Items.blastCompound;
unitAmount = 2; unitAmount = 2;
spacing = 2;
unitScaling = 3; unitScaling = 3;
max = 8; max = 8;
}}, }},

View File

@@ -123,7 +123,7 @@ public class OverlayRenderer {
drawbars.run(); drawbars.run();
if(values[0] > 0){ if(values[0] > 0){
drawEncloser(target.drawx(), target.drawy() + block.size * tilesize/2f + 2f + values[0]/2f - 0.5f + (values[0] > 2 ? 0.5f : 0), values[0]); drawEncloser(target.drawx(), target.drawy() + block.size * tilesize/2f + 2f + values[0]/2f - 0.5f + (values[0] > 1 ? 0.75f : 0), values[0]);
} }
if(values[1] > 0){ if(values[1] > 0){

View File

@@ -353,7 +353,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile != null){ if(tile != null){
//draw placing //draw placing
if(mode == placing) { if(mode == placing && recipe != null) {
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale); NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y); Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
@@ -465,7 +465,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if (tile == null) return false; if (tile == null) return false;
if(mode == placing) { if(mode == placing && recipe != null) {
//normalize area //normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100); NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100);
@@ -599,6 +599,10 @@ public class MobileInput extends InputHandler implements GestureListener{
selection.clear(); selection.clear();
} }
if(lineMode && mode == placing && recipe == null){
lineMode = false;
}
//if there is no mode and there's a recipe, switch to placing //if there is no mode and there's a recipe, switch to placing
if(recipe != null && mode == none){ if(recipe != null && mode == none){
mode = placing; mode = placing;

View File

@@ -19,7 +19,6 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Bits;
import io.anuke.ucore.util.Log;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -51,7 +50,6 @@ public class Save16 extends SaveFileVersion {
state.difficulty = Difficulty.values()[difficulty]; state.difficulty = Difficulty.values()[difficulty];
state.mode = GameMode.values()[mode]; state.mode = GameMode.values()[mode];
state.enemies = 0; //TODO display enemies correctly!
state.wave = wave; state.wave = wave;
state.wavetime = wavetime; state.wavetime = wavetime;

View File

@@ -31,7 +31,6 @@ public class Net{
private static boolean active; private static boolean active;
private static boolean clientLoaded; private static boolean clientLoaded;
private static Array<Object> packetQueue = new Array<>(); private static Array<Object> packetQueue = new Array<>();
private static ObjectMap<Class<?>, Consumer> listeners = new ObjectMap<>();
private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>(); private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>();
private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>(); private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>();
private static ClientProvider clientProvider; private static ClientProvider clientProvider;
@@ -146,11 +145,6 @@ public class Net{
Net.serverProvider = provider; Net.serverProvider = provider;
} }
/**Registers a common listener for when an object is recieved. Fired on both client and serve.r*/
public static <T> void handle(Class<T> type, Consumer<T> listener){
listeners.put(type, listener);
}
/**Registers a client listener for when an object is recieved.*/ /**Registers a client listener for when an object is recieved.*/
public static <T> void handleClient(Class<T> type, Consumer<T> listener){ public static <T> void handleClient(Class<T> type, Consumer<T> listener){
clientListeners.put(type, listener); clientListeners.put(type, listener);
@@ -178,12 +172,10 @@ public class Net{
streams.remove(builder.id); streams.remove(builder.id);
handleClientReceived(builder.build()); handleClientReceived(builder.build());
} }
}else if(clientListeners.get(object.getClass()) != null || }else if(clientListeners.get(object.getClass()) != null){
listeners.get(object.getClass()) != null){
if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){ if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){
if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object); if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object);
if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object);
synchronized (packetPoolLock) { synchronized (packetPoolLock) {
Pooling.free(object); Pooling.free(object);
} }
@@ -203,9 +195,8 @@ public class Net{
/**Call to handle a packet being recieved for the server.*/ /**Call to handle a packet being recieved for the server.*/
public static void handleServerReceived(int connection, Object object){ public static void handleServerReceived(int connection, Object object){
if(serverListeners.get(object.getClass()) != null || listeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object); 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);
synchronized (packetPoolLock) { synchronized (packetPoolLock) {
Pooling.free(object); Pooling.free(object);
} }

View File

@@ -179,7 +179,7 @@ public class Packets {
} }
public enum AdminAction{ public enum AdminAction{
kick, ban, trace kick, ban, trace, wave
} }
/**Marks the beginning of a stream.*/ /**Marks the beginning of a stream.*/

View File

@@ -4,19 +4,20 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.EventType.ResizeEvent;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.ui.BorderImage; import io.anuke.mindustry.ui.BorderImage;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.event.Touchable; import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.ButtonGroup;
import io.anuke.ucore.scene.ui.ImageButton; import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.utils.Cursors; import io.anuke.ucore.scene.utils.Cursors;
import io.anuke.ucore.scene.utils.Elements;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -42,7 +43,7 @@ public class LevelDialog extends FloatingDialog{
int maxwidth = (Gdx.graphics.getHeight() > Gdx.graphics.getHeight() ? 2 : 4); int maxwidth = (Gdx.graphics.getHeight() > Gdx.graphics.getHeight() ? 2 : 4);
/*Table selmode = new Table(); Table selmode = new Table();
ButtonGroup<TextButton> group = new ButtonGroup<>(); ButtonGroup<TextButton> group = new ButtonGroup<>();
selmode.add("$text.level.mode").padRight(15f); selmode.add("$text.level.mode").padRight(15f);
@@ -50,14 +51,13 @@ public class LevelDialog extends FloatingDialog{
TextButton[] b = {null}; TextButton[] b = {null};
b[0] = Elements.newButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode); b[0] = Elements.newButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode);
b[0].update(() -> b[0].setChecked(state.mode == mode)); b[0].update(() -> b[0].setChecked(state.mode == mode));
b[0].setDisabled(true);
group.add(b[0]); group.add(b[0]);
selmode.add(b[0]).size(130f, 54f); selmode.add(b[0]).size(130f, 54f);
} }
selmode.addButton("?", this::displayGameModeHelp).size(50f, 54f).padLeft(18f); selmode.addButton("?", this::displayGameModeHelp).size(50f, 54f).padLeft(18f);
content().add(selmode); content().add(selmode);
content().row();*/ content().row();
Difficulty[] ds = Difficulty.values(); Difficulty[] ds = Difficulty.values();

View File

@@ -181,6 +181,8 @@ public class BlockInventoryFragment extends Fragment {
@Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true) @Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true)
public static void requestItem(Player player, Tile tile, Item item, int amount){ public static void requestItem(Player player, Tile tile, Item item, int amount){
if(player == null) return;
int removed = tile.block().removeStack(tile, item, amount); int removed = tile.block().removeStack(tile, item, amount);
player.inventory.addItem(item, removed); player.inventory.addItem(item, removed);

View File

@@ -185,7 +185,7 @@ public class DebugFragment extends Fragment {
result.append(player.id); result.append(player.id);
result.append("\n"); result.append("\n");
result.append(" cid: "); result.append(" cid: ");
result.append(player.con.id); result.append(player.con == null ? -1 : player.con.id);
result.append("\n"); result.append("\n");
result.append(" dead: "); result.append(" dead: ");
result.append(player.isDead()); result.append(player.isDead());

View File

@@ -7,7 +7,10 @@ import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Scaling; import com.badlogic.gdx.utils.Scaling;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.IntFormat; import io.anuke.mindustry.ui.IntFormat;
import io.anuke.mindustry.ui.Minimap; import io.anuke.mindustry.ui.Minimap;
@@ -288,10 +291,11 @@ public class HudFragment extends Fragment{
} }
private String getEnemiesRemaining() { private String getEnemiesRemaining() {
if(state.enemies == 1) { int enemies = unitGroups[Team.red.ordinal()].size();
return Bundles.format("text.enemies.single", state.enemies); if(enemies == 1) {
return Bundles.format("text.enemies.single", enemies);
} else { } else {
return Bundles.format("text.enemies", state.enemies); return Bundles.format("text.enemies", enemies);
} }
} }
@@ -310,7 +314,7 @@ public class HudFragment extends Fragment{
row(); row();
new label(() -> state.enemies > 0 ? new label(() -> unitGroups[Team.red.ordinal()].size() > 0 && state.mode.disableWaveTimer ?
getEnemiesRemaining() : getEnemiesRemaining() :
(state.mode.disableWaveTimer) ? "$text.waiting" (state.mode.disableWaveTimer) ? "$text.waiting"
: timef.get((int) (state.wavetime / 60f))) : timef.get((int) (state.wavetime / 60f)))
@@ -329,14 +333,17 @@ public class HudFragment extends Fragment{
private void playButton(float uheight){ private void playButton(float uheight){
new imagebutton("icon-play", 30f, () -> { new imagebutton("icon-play", 30f, () -> {
if(Net.client() && players[0].isAdmin){
Call.onAdminRequest(players[0], AdminAction.wave);
}else {
state.wavetime = 0f; state.wavetime = 0f;
}
}).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padLeft(-15).padRight(-10).width(40f).update(l->{ }).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padLeft(-15).padRight(-10).width(40f).update(l->{
boolean vis = state.mode.disableWaveTimer && (Net.server() || !Net.active()); boolean vis = state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active());
boolean paused = state.is(State.paused) || !vis; boolean paused = state.is(State.paused) || !vis;
l.setVisible(vis);
l.getStyle().imageUp = Core.skin.getDrawable(vis ? "icon-play" : "clear"); l.getStyle().imageUp = Core.skin.getDrawable(vis ? "icon-play" : "clear");
l.setTouchable(!paused ? Touchable.enabled : Touchable.disabled); l.setTouchable(!paused ? Touchable.enabled : Touchable.disabled);
}); }).visible(() -> state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active()) && unitGroups[Team.red.ordinal()].size() == 0);
} }
} }

View File

@@ -432,7 +432,7 @@ public class Block extends BaseBlock implements Content{
if(shadowRegions != null) { if(shadowRegions != null) {
Draw.rect(shadowRegions[(Mathf.randomSeed(tile.id(), 0, variants - 1))], tile.worldx(), tile.worldy()); Draw.rect(shadowRegions[(Mathf.randomSeed(tile.id(), 0, variants - 1))], tile.worldx(), tile.worldy());
}else{ }else if(shadowRegion != null){
Draw.rect(shadowRegion, tile.drawx(), tile.drawy()); Draw.rect(shadowRegion, tile.drawx(), tile.drawy());
} }
} }

View File

@@ -76,7 +76,7 @@ public class BreakBlock extends Block {
public void afterDestroyed(Tile tile, TileEntity e){ public void afterDestroyed(Tile tile, TileEntity e){
BreakEntity entity = (BreakEntity)e; BreakEntity entity = (BreakEntity)e;
if(entity.previous.synthetic()){ if(entity != null && entity.previous != null && entity.previous.synthetic()){
tile.setBlock(entity.previous); tile.setBlock(entity.previous);
} }
} }
@@ -145,8 +145,10 @@ public class BreakBlock extends Block {
if(tile.entity instanceof BreakEntity){ if(tile.entity instanceof BreakEntity){
BreakEntity entity = tile.entity(); BreakEntity entity = tile.entity();
if(entity.previous != null){
Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), entity.previous.size); Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), entity.previous.size);
} }
}
world.removeBlock(tile); world.removeBlock(tile);
} }
@@ -186,6 +188,10 @@ public class BreakBlock extends Block {
} }
progress += add; progress += add;
if(progress > 1.0001f){
progress = 1.0001f;
}
} }
public float progress(){ public float progress(){

View File

@@ -48,7 +48,7 @@ public class BuildBlock extends Block {
@Override @Override
public boolean isSolidFor(Tile tile) { public boolean isSolidFor(Tile tile) {
BuildEntity entity = tile.entity(); BuildEntity entity = tile.entity();
return entity.recipe.result.solid || entity.previous.solid; return entity == null || entity.recipe ==null || entity.recipe.result.solid || entity.previous.solid;
} }
@Override @Override
@@ -88,7 +88,7 @@ public class BuildBlock extends Block {
public void draw(Tile tile){ public void draw(Tile tile){
BuildEntity entity = tile.entity(); BuildEntity entity = tile.entity();
if(entity.previous.synthetic()) { if(entity.previous != null && entity.previous.synthetic()) {
for (TextureRegion region : entity.previous.getBlockIcon()) { for (TextureRegion region : entity.previous.getBlockIcon()) {
Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0); Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0);
} }
@@ -131,7 +131,7 @@ public class BuildBlock extends Block {
CallBlocks.onBuildDeath(tile); CallBlocks.onBuildDeath(tile);
} }
if(!entity.updated){ if(!entity.updated && entity.recipe != null){
entity.progress -= 1f/entity.recipe.cost/decaySpeedScl; entity.progress -= 1f/entity.recipe.cost/decaySpeedScl;
} }
@@ -208,6 +208,10 @@ public class BuildBlock extends Block {
lastProgress = maxProgress; lastProgress = maxProgress;
updated = true; updated = true;
if(progress > 1.0001f){
progress = 1.0001f;
}
} }
public double checkRequired(InventoryModule inventory, double amount){ public double checkRequired(InventoryModule inventory, double amount){

View File

@@ -41,6 +41,13 @@ public class ItemTurret extends CooledTurret {
return Math.min((int)((maxAmmo - entity.totalAmmo) / ammoMap.get(item).quantityMultiplier), amount); return Math.min((int)((maxAmmo - entity.totalAmmo) / ammoMap.get(item).quantityMultiplier), amount);
} }
@Override
public void handleStack(Item item, int amount, Tile tile, Unit source){
for (int i = 0; i < amount; i++) {
handleItem(item, tile, null);
}
}
//currently can't remove items from turrets. //currently can't remove items from turrets.
@Override @Override
public int removeStack(Tile tile, Item item, int amount) { public int removeStack(Tile tile, Item item, int amount) {

View File

@@ -51,7 +51,9 @@ public class PowerDistributor extends PowerBlock {
} }
protected boolean shouldDistribute(Tile tile, Tile other) { protected boolean shouldDistribute(Tile tile, Tile other) {
return !(other.block() instanceof PowerGenerator) || other.entity.power.amount / other.block().powerCapacity < tile.entity.power.amount / powerCapacity; //only generators can distribute to other generators
return (!(other.block() instanceof PowerGenerator) || tile.block() instanceof PowerGenerator)
&& other.entity.power.amount / other.block().powerCapacity < tile.entity.power.amount / powerCapacity;
} }
@Override @Override

View File

@@ -129,14 +129,14 @@ public class PowerNode extends PowerBlock{
Tile link = world.tile(x, y); Tile link = world.tile(x, y);
if(link != null) link = link.target(); if(link != null) link = link.target();
if(link != tile && linkValid(tile, link)){ if(link != tile && linkValid(tile, link, false)){
boolean linked = linked(tile, link); boolean linked = linked(tile, link);
Draw.color(linked ? Palette.place : Palette.breakInvalid); Draw.color(linked ? Palette.place : Palette.breakInvalid);
Lines.square(link.drawx(), link.drawy(), Lines.square(link.drawx(), link.drawy(),
link.block().size * tilesize / 2f + 1f + (linked ? 0f : Mathf.absin(Timers.time(), 4f, 1f))); link.block().size * tilesize / 2f + 1f + (linked ? 0f : Mathf.absin(Timers.time(), 4f, 1f)));
if(entity.links.size >= maxNodes && !linked){ if((entity.links.size >= maxNodes || (link.block() instanceof PowerNode && ((DistributorEntity)link.entity).links.size >= ((PowerNode)link.block()).maxNodes)) && !linked){
Draw.color(); Draw.color();
Draw.rect("cross-" + link.block().size, link.drawx(), link.drawy()); Draw.rect("cross-" + link.block().size, link.drawx(), link.drawy());
} }
@@ -190,11 +190,13 @@ public class PowerNode extends PowerBlock{
} }
protected boolean shouldDistribute(Tile tile, Tile other) { protected boolean shouldDistribute(Tile tile, Tile other) {
return other.entity.power.amount / other.block().powerCapacity <= tile.entity.power.amount / powerCapacity; return other.entity.power.amount / other.block().powerCapacity <= tile.entity.power.amount / powerCapacity &&
!(other.block() instanceof PowerGenerator); //do not distribute to power generators
} }
protected boolean shouldLeechPower(Tile tile, Tile other){ protected boolean shouldLeechPower(Tile tile, Tile other){
return !(other.block() instanceof PowerNode) return !(other.block() instanceof PowerNode)
&& other.block() instanceof PowerDistributor //only suck power from batteries and power generators
&& other.entity.power.amount / other.block().powerCapacity > tile.entity.power.amount / powerCapacity; && other.entity.power.amount / other.block().powerCapacity > tile.entity.power.amount / powerCapacity;
} }
@@ -242,6 +244,10 @@ public class PowerNode extends PowerBlock{
} }
protected boolean linkValid(Tile tile, Tile link){ protected boolean linkValid(Tile tile, Tile link){
return linkValid(tile, link, true);
}
protected boolean linkValid(Tile tile, Tile link, boolean checkMaxNodes){
if(!(tile != link && link != null && link.block().hasPower)) return false; if(!(tile != link && link != null && link.block().hasPower)) return false;
if(link.block() instanceof PowerNode){ if(link.block() instanceof PowerNode){
@@ -250,7 +256,7 @@ public class PowerNode extends PowerBlock{
return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) <= Math.max(laserRange * tilesize, return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) <= Math.max(laserRange * tilesize,
((PowerNode)link.block()).laserRange * tilesize) - tilesize/2f ((PowerNode)link.block()).laserRange * tilesize) - tilesize/2f
+ (link.block().size-1)*tilesize/2f + (tile.block().size-1)*tilesize/2f && + (link.block().size-1)*tilesize/2f + (tile.block().size-1)*tilesize/2f &&
(oe.links.size < ((PowerNode)link.block()).maxNodes || oe.links.contains(tile.packedPosition())); (!checkMaxNodes || (oe.links.size < ((PowerNode)link.block()).maxNodes || oe.links.contains(tile.packedPosition())));
}else{ }else{
return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy())
<= laserRange * tilesize - tilesize/2f + (link.block().size-1)*tilesize; <= laserRange * tilesize - tilesize/2f + (link.block().size-1)*tilesize;
@@ -276,8 +282,9 @@ public class PowerNode extends PowerBlock{
return new DistributorEntity(); return new DistributorEntity();
} }
@Remote(targets = Loc.both, called = Loc.both, in = In.blocks, forward = true) @Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true)
public static void linkPowerDistributors(Player player, Tile tile, Tile other){ public static void linkPowerDistributors(Player player, Tile tile, Tile other){
DistributorEntity entity = tile.entity(); DistributorEntity entity = tile.entity();
if(!entity.links.contains(other.packedPosition())){ if(!entity.links.contains(other.packedPosition())){

View File

@@ -154,6 +154,9 @@ public class MechFactory extends Block{
MechFactoryEntity entity = tile.entity(); MechFactoryEntity entity = tile.entity();
Effects.effect(Fx.spawn, entity); Effects.effect(Fx.spawn, entity);
if(entity.player == null) return;
Mech result = ((MechFactory)tile.block()).mech; Mech result = ((MechFactory)tile.block()).mech;
if(entity.player.mech == result){ if(entity.player.mech == result){

View File

@@ -26,9 +26,6 @@ public class ByteSerializer implements Serialization {
throw new RuntimeException("Unregistered class: " + ClassReflection.getSimpleName(o.getClass())); throw new RuntimeException("Unregistered class: " + ClassReflection.getSimpleName(o.getClass()));
byteBuffer.put(id); byteBuffer.put(id);
((Packet) o).write(byteBuffer); ((Packet) o).write(byteBuffer);
synchronized (packetPoolLock) {
Pooling.free(o);
}
} }
} }

View File

@@ -15,6 +15,7 @@ import io.anuke.mindustry.net.NetworkIO;
import io.anuke.mindustry.net.Packets.Connect; import io.anuke.mindustry.net.Packets.Connect;
import io.anuke.mindustry.net.Packets.Disconnect; import io.anuke.mindustry.net.Packets.Disconnect;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Pooling;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import java.io.IOException; import java.io.IOException;
@@ -26,6 +27,7 @@ import java.nio.channels.ClosedSelectorException;
import java.util.List; import java.util.List;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.net.Net.packetPoolLock;
public class KryoClient implements ClientProvider{ public class KryoClient implements ClientProvider{
Client client; Client client;
@@ -134,6 +136,10 @@ public class KryoClient implements ClientProvider{
}else{ }else{
client.sendUDP(object); client.sendUDP(object);
} }
synchronized (packetPoolLock) {
Pooling.free(object);
}
} }
@Override @Override

View File

@@ -6,9 +6,7 @@ import io.anuke.ucore.util.ColorCodes;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static io.anuke.mindustry.Vars.headless; import static io.anuke.mindustry.Vars.headless;
@@ -65,25 +63,4 @@ public class KryoCore {
private static int calculateLag() { private static int calculateLag() {
return fakeLagMin + (int)(Math.random() * (fakeLagMax - fakeLagMin)); return fakeLagMin + (int)(Math.random() * (fakeLagMax - fakeLagMin));
} }
/**Executes something in a potentially unreliable way. Used to simulate lag and packet errors with UDP.*/
public static void recieveUnreliable(Runnable run){
if(fakeLag && threadPool == null){
threadPool = Executors.newScheduledThreadPool(1, r -> {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
});
}
if(fakeLag){
do {
if (Math.random() >= fakeLagDrop) {
threadPool.schedule(run, calculateLag(), TimeUnit.MILLISECONDS);
}
} while (Math.random() < fakeLagDuplicate);
}else{
run.run();
}
}
} }

View File

@@ -15,10 +15,7 @@ import io.anuke.mindustry.net.Net.SendMode;
import io.anuke.mindustry.net.Net.ServerProvider; import io.anuke.mindustry.net.Net.ServerProvider;
import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.NetworkIO;
import io.anuke.mindustry.net.Packets.Connect; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.net.Packets.Disconnect;
import io.anuke.mindustry.net.Packets.StreamBegin;
import io.anuke.mindustry.net.Packets.StreamChunk;
import io.anuke.mindustry.net.Streamable; import io.anuke.mindustry.net.Streamable;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;

View File

@@ -8,6 +8,7 @@ import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.EventType.GameOverEvent; import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.io.SaveIO;
@@ -138,7 +139,7 @@ public class ServerControl extends Module {
Log.info("Stopped server."); Log.info("Stopped server.");
}); });
handler.register("host", "[mapname] [mode]", "Open the server with a specific map.", arg -> { handler.register("host", "[mode] [mapname]", "Open the server with a specific map.", arg -> {
if(state.is(State.playing)){ if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first."); err("Already hosting. Type 'stop' to stop hosting first.");
return; return;
@@ -147,34 +148,39 @@ public class ServerControl extends Module {
Map result = null; Map result = null;
if(arg.length > 0) { if(arg.length > 0) {
String search = arg[0];
for (Map map : world.maps().all()) {
if (map.name.equalsIgnoreCase(search))
result = map;
}
if(result == null){
err("No map with name &y'{0}'&lr found.", search);
return;
}
GameMode mode; GameMode mode;
try{ try{
mode = arg.length < 2 ? GameMode.waves : GameMode.valueOf(arg[1]); mode = GameMode.valueOf(arg[0]);
}catch (IllegalArgumentException e){ }catch (IllegalArgumentException e){
err("No gamemode '{0}' found.", arg[1]); err("No gamemode '{0}' found.", arg[0]);
return; return;
} }
info("Loading map..."); info("Loading map...");
state.mode = mode; state.mode = mode;
if(arg.length > 1) {
String search = arg[1];
for (Map map : world.maps().all()) {
if (map.name.equalsIgnoreCase(search))
result = map;
}
if (result == null) {
err("No map with name &y'{0}'&lr found.", search);
return;
}
logic.reset(); logic.reset();
world.loadMap(result); world.loadMap(result);
}else{
Log.info("&ly&fiNo map specified, so a procedural one was generated.");
world.loadProceduralMap();
}
}else{ }else{
world.loadProceduralMap();
Log.info("&ly&fiNo map specified, so a procedural one was generated."); Log.info("&ly&fiNo map specified, so a procedural one was generated.");
world.loadProceduralMap();
} }
logic.play(); logic.play();
@@ -194,10 +200,10 @@ public class ServerControl extends Module {
if(state.is(State.menu)){ if(state.is(State.menu)){
info("&lyStatus: &rserver closed"); info("&lyStatus: &rserver closed");
}else{ }else{
info("&lyStatus: &lcPlaying on map &fi{0}&fb &lb/&lc Wave {1} &lb/&lc {2}", info("&lyStatus: &lcPlaying on map &fi{0}&fb &lb/&lc Wave {1} &lb/&lc {2} &lb/&lc {3}",
Strings.capitalize(world.getMap().name), state.wave, Strings.capitalize(state.difficulty.name())); Strings.capitalize(world.getMap().name), state.wave, Strings.capitalize(state.difficulty.name()), Strings.capitalize(state.mode.name()));
if(state.enemies > 0){ if(state.mode.disableWaveTimer){
info("&ly{0} enemies remaining.", state.enemies); info("&ly{0} enemies.", unitGroups[Team.red.ordinal()].size());
}else{ }else{
info("&ly{0} seconds until next wave.", (int)(state.wavetime / 60)); info("&ly{0} seconds until next wave.", (int)(state.wavetime / 60));
} }
@@ -221,7 +227,7 @@ public class ServerControl extends Module {
if(playerGroup.size() > 0) { if(playerGroup.size() > 0) {
info("&lyPlayers: {0}", playerGroup.size()); info("&lyPlayers: {0}", playerGroup.size());
for (Player p : playerGroup.all()) { for (Player p : playerGroup.all()) {
print(" &y{0} / Connection {1} / IP: {2}", p.name, p.con, p.con.address); print(" &y{0} / Connection {1} / IP: {2}", p.name, p.con.id, p.con.address);
} }
}else{ }else{
info("&lyNo players connected."); info("&lyNo players connected.");
@@ -464,6 +470,7 @@ public class ServerControl extends Module {
if(target != null){ if(target != null){
netServer.admins.adminPlayer(target.uuid, target.usid); netServer.admins.adminPlayer(target.uuid, target.usid);
target.isAdmin = true;
info("Admin-ed player by ID: {0} / {1}", target.uuid, arg[0]); info("Admin-ed player by ID: {0} / {1}", target.uuid, arg[0]);
}else{ }else{
info("Nobody with that name could be found."); info("Nobody with that name could be found.");
@@ -487,6 +494,7 @@ public class ServerControl extends Module {
if(target != null){ if(target != null){
netServer.admins.unAdminPlayer(target.uuid); netServer.admins.unAdminPlayer(target.uuid);
target.isAdmin = false;
info("Un-admin-ed player by ID: {0} / {1}", target.uuid, arg[0]); info("Un-admin-ed player by ID: {0} / {1}", target.uuid, arg[0]);
}else{ }else{
info("Nobody with that name could be found."); info("Nobody with that name could be found.");
@@ -509,8 +517,6 @@ public class ServerControl extends Module {
handler.register("runwave", "Trigger the next wave.", arg -> { handler.register("runwave", "Trigger the next wave.", arg -> {
if(!state.is(State.playing)) { if(!state.is(State.playing)) {
err("Not hosting. Host a game first."); err("Not hosting. Host a game first.");
}else if(state.enemies > 0){
err("There are still {0} enemies remaining.", state.enemies);
}else{ }else{
logic.runWave(); logic.runWave();
info("Wave spawned."); info("Wave spawned.");