Compare commits

...

13 Commits
v90 ... v91

Author SHA1 Message Date
Anuken
0a471441cc Balance 2019-08-25 14:23:43 -04:00
Anuken
73149123b4 Fullscreen key / Bugfixes 2019-08-25 12:02:51 -04:00
Anuken
5c6f2171f4 Fixed black tile issue 2019-08-25 11:16:38 -04:00
Anuken
fd973038bb mnet was a mistake 2019-08-24 18:29:21 -04:00
Anuken
b7dff91d01 Merge remote-tracking branch 'origin/master' 2019-08-24 15:39:22 -04:00
Anuken
e314d199f0 By popular demand, re-added command center 2019-08-24 15:39:01 -04:00
Spico (Spirit Guy)
3938c199f6 Request to Reseting the Turkish Translation File (#609)
There were some conflicting duplicated lines and someone put the file onto google translate, so some of the coloured lines were broken, and since (I think) Im the only one who edits this file (My brother didnt started translating yet) It would be easier for me to start from scratch.
2019-08-24 14:41:59 -04:00
Anuken
962eef4a1d or not 2019-08-24 13:06:39 -04:00
Anuken
97443a86cb fixed a crash that shouldn't even happen 2019-08-24 11:52:14 -04:00
Anuken
7137616224 Merge remote-tracking branch 'origin/master' 2019-08-24 10:45:36 -04:00
Anuken
444825a5c1 Bugfix 2019-08-24 10:45:25 -04:00
Anuken
986deaa6c7 Various fixes 2019-08-24 10:32:29 -04:00
Anuken
7b27903de2 Fixed some static library issues 2019-08-24 10:00:04 -04:00
54 changed files with 4117 additions and 3754 deletions

View File

@@ -129,8 +129,8 @@ public class AndroidLauncher extends AndroidApplication{
} }
config.hideStatusBar = true; config.hideStatusBar = true;
Net.setClientProvider(new MClient()); Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new MServer()); Net.setServerProvider(new ArcNetServer());
initialize(new Mindustry(), config); initialize(new Mindustry(), config);
checkFiles(getIntent()); checkFiles(getIntent());
} }

View File

@@ -232,7 +232,7 @@ project(":core"){
compile "org.lz4:lz4-java:1.4.1" compile "org.lz4:lz4-java:1.4.1"
compile arcModule("arc-core") compile arcModule("arc-core")
compile arcModule("extensions:freetype") compile arcModule("extensions:freetype")
compile arcModule("extensions:mnet") compile arcModule("extensions:arcnet")
if(localArc() && debugged()) compile arcModule("extensions:recorder") if(localArc() && debugged()) compile arcModule("extensions:recorder")
compileOnly project(":annotations") compileOnly project(":annotations")

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -25,9 +25,6 @@ stat.deconstructed = Buildings Deconstructed:[accent] {0}
stat.delivered = Resources Launched: stat.delivered = Resources Launched:
stat.rank = Final Rank: [accent]{0} stat.rank = Final Rank: [accent]{0}
placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\n\n[scarlet]DO IT.
removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\n\n[scarlet]DO IT.
launcheditems = [accent]Launched Items launcheditems = [accent]Launched Items
map.delete = Are you sure you want to delete the map "[accent]{0}[]"? map.delete = Are you sure you want to delete the map "[accent]{0}[]"?
level.highscore = High Score: [accent]{0} level.highscore = High Score: [accent]{0}
@@ -541,6 +538,7 @@ keybind.press.axis = Press an axis or key...
keybind.screenshot.name = Map Screenshot keybind.screenshot.name = Map Screenshot
keybind.move_x.name = Move x keybind.move_x.name = Move x
keybind.move_y.name = Move y keybind.move_y.name = Move y
keybind.fullscreen.name = Toggle Fullscreen
keybind.select.name = Select/Shoot keybind.select.name = Select/Shoot
keybind.diagonal_placement.name = Diagonal Placement keybind.diagonal_placement.name = Diagonal Placement
keybind.pick.name = Pick Block keybind.pick.name = Pick Block
@@ -807,6 +805,7 @@ block.blast-mixer.name = Blast Mixer
block.solar-panel.name = Solar Panel block.solar-panel.name = Solar Panel
block.solar-panel-large.name = Large Solar Panel block.solar-panel-large.name = Large Solar Panel
block.oil-extractor.name = Oil Extractor block.oil-extractor.name = Oil Extractor
block.command-center.name = Command Center
block.draug-factory.name = Draug Miner Drone Factory block.draug-factory.name = Draug Miner Drone Factory
block.spirit-factory.name = Spirit Repair Drone Factory block.spirit-factory.name = Spirit Repair Drone Factory
block.phantom-factory.name = Phantom Builder Drone Factory block.phantom-factory.name = Phantom Builder Drone Factory
@@ -1031,6 +1030,7 @@ block.ripple.description = An extremely powerful artillery turret. Shoots cluste
block.cyclone.description = A large anti-air and anti-ground turret. Fires explosive clumps of flak at nearby units. block.cyclone.description = A large anti-air and anti-ground turret. Fires explosive clumps of flak at nearby units.
block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets.
block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate.
block.command-center.description = Issues movement commands to allied units across the map.\nCauses units to patrol, attack an enemy core or retreat to the core/factory. When no enemy core is present, units will default to patrolling under the attack command.
block.draug-factory.description = Produces Draug mining drones. block.draug-factory.description = Produces Draug mining drones.
block.spirit-factory.description = Produces Spirit structural repair drones. block.spirit-factory.description = Produces Spirit structural repair drones.
block.phantom-factory.description = Produces advanced construction drones. block.phantom-factory.description = Produces advanced construction drones.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 718 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 KiB

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

After

Width:  |  Height:  |  Size: 581 KiB

View File

@@ -1,16 +1,14 @@
package io.anuke.mindustry.content; package io.anuke.mindustry.content;
import io.anuke.arc.Core; import io.anuke.arc.*;
import io.anuke.arc.function.BooleanProvider; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.*;
import io.anuke.arc.util.Tmp; import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.*;
import io.anuke.mindustry.entities.Damage; import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.*;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
@@ -24,16 +22,13 @@ import io.anuke.mindustry.world.blocks.production.*;
import io.anuke.mindustry.world.blocks.sandbox.*; import io.anuke.mindustry.world.blocks.sandbox.*;
import io.anuke.mindustry.world.blocks.storage.*; import io.anuke.mindustry.world.blocks.storage.*;
import io.anuke.mindustry.world.blocks.units.*; import io.anuke.mindustry.world.blocks.units.*;
import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; import io.anuke.mindustry.world.consumers.*;
import io.anuke.mindustry.world.meta.Attribute; import io.anuke.mindustry.world.meta.*;
import io.anuke.mindustry.world.modules.LiquidModule; import io.anuke.mindustry.world.modules.*;
import static io.anuke.mindustry.Vars.state; import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.world;
public class Blocks implements ContentList{ public class Blocks implements ContentList{
public static final BooleanProvider padVisible = () -> state.rules.attackMode || state.rules.pvp || state.isEditor();
public static Block public static Block
//environment //environment
@@ -79,7 +74,7 @@ public class Blocks implements ContentList{
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown, duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown,
//units //units
draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory, commandCenter, draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory,
fortressFactory, repairPoint, fortressFactory, repairPoint,
//upgrades //upgrades
@@ -1648,17 +1643,23 @@ public class Blocks implements ContentList{
consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 20), new ItemStack(Items.titanium, 10)); consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 20), new ItemStack(Items.titanium, 10));
}}; }};
wraithFactory = new UnitFactory("wraith-factory"){{ commandCenter = new CommandCenter("command-center"){{
requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45)); requirements(Category.units, ItemStack.with(Items.copper, 200, Items.lead, 250, Items.silicon, 250, Items.graphite, 100));
type = UnitTypes.wraith;
produceTime = 750;
size = 2; size = 2;
consumes.power(0.6f); health = size * size * 55;
}};
wraithFactory = new UnitFactory("wraith-factory"){{
requirements(Category.units, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45));
type = UnitTypes.wraith;
produceTime = 700;
size = 2;
consumes.power(0.5f);
consumes.items(new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 5)); consumes.items(new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 5));
}}; }};
ghoulFactory = new UnitFactory("ghoul-factory"){{ ghoulFactory = new UnitFactory("ghoul-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110)); requirements(Category.units, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110));
type = UnitTypes.ghoul; type = UnitTypes.ghoul;
produceTime = 1150; produceTime = 1150;
size = 3; size = 3;
@@ -1667,7 +1668,7 @@ public class Blocks implements ContentList{
}}; }};
revenantFactory = new UnitFactory("revenant-factory"){{ revenantFactory = new UnitFactory("revenant-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200)); requirements(Category.units, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200));
type = UnitTypes.revenant; type = UnitTypes.revenant;
produceTime = 2000; produceTime = 2000;
size = 4; size = 4;
@@ -1676,7 +1677,7 @@ public class Blocks implements ContentList{
}}; }};
daggerFactory = new UnitFactory("dagger-factory"){{ daggerFactory = new UnitFactory("dagger-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.lead, 55, Items.silicon, 35)); requirements(Category.units, ItemStack.with(Items.lead, 55, Items.silicon, 35));
type = UnitTypes.dagger; type = UnitTypes.dagger;
produceTime = 850; produceTime = 850;
size = 2; size = 2;
@@ -1685,17 +1686,17 @@ public class Blocks implements ContentList{
}}; }};
crawlerFactory = new UnitFactory("crawler-factory"){{ crawlerFactory = new UnitFactory("crawler-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.lead, 25, Items.silicon, 30)); requirements(Category.units, ItemStack.with(Items.lead, 45, Items.silicon, 30));
type = UnitTypes.crawler; type = UnitTypes.crawler;
produceTime = 250; produceTime = 300;
size = 2; size = 2;
maxSpawn = 8; maxSpawn = 8;
consumes.power(0.4f); consumes.power(0.4f);
consumes.items(new ItemStack(Items.coal, 5)); consumes.items(new ItemStack(Items.coal, 10));
}}; }};
titanFactory = new UnitFactory("titan-factory"){{ titanFactory = new UnitFactory("titan-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45)); requirements(Category.units, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45));
type = UnitTypes.titan; type = UnitTypes.titan;
produceTime = 1050; produceTime = 1050;
size = 3; size = 3;
@@ -1704,7 +1705,7 @@ public class Blocks implements ContentList{
}}; }};
fortressFactory = new UnitFactory("fortress-factory"){{ fortressFactory = new UnitFactory("fortress-factory"){{
requirements(Category.units, padVisible, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75)); requirements(Category.units, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75));
type = UnitTypes.fortress; type = UnitTypes.fortress;
produceTime = 2000; produceTime = 2000;
size = 3; size = 3;

View File

@@ -32,7 +32,7 @@ public class Fx implements ContentList{
bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke, shootSmall, shootHeal, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke, bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke, shootSmall, shootHeal, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke,
shootBigSmoke2, shootSmallFlame, shootPyraFlame, shootLiquid, shellEjectSmall, shellEjectMedium, shootBigSmoke2, shootSmallFlame, shootPyraFlame, shootLiquid, shellEjectSmall, shellEjectMedium,
shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot, shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot,
unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch; unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch, commandSend;
@Override @Override
public void load(){ public void load(){
@@ -53,6 +53,13 @@ public class Fx implements ContentList{
Draw.reset(); Draw.reset();
}); });
commandSend = new Effect(28, e -> {
Draw.color(Pal.command);
Lines.stroke(e.fout() * 2f);
Lines.circle(e.x, e.y, 4f + e.finpow() * 120f);
Draw.color();
});
placeBlock = new Effect(16, e -> { placeBlock = new Effect(16, e -> {
Draw.color(Pal.accent); Draw.color(Pal.accent);
Lines.stroke(3f - e.fin() * 2f); Lines.stroke(3f - e.fin() * 2f);

View File

@@ -254,6 +254,7 @@ public class TechTree implements ContentList{
}); });
node(daggerFactory, () -> { node(daggerFactory, () -> {
node(commandCenter, () -> {});
node(crawlerFactory, () -> { node(crawlerFactory, () -> {
node(titanFactory, () -> { node(titanFactory, () -> {
node(fortressFactory, () -> { node(fortressFactory, () -> {
@@ -296,7 +297,7 @@ public class TechTree implements ContentList{
private TechNode node(Block block, Runnable children){ private TechNode node(Block block, Runnable children){
ItemStack[] requirements = new ItemStack[block.buildRequirements.length]; ItemStack[] requirements = new ItemStack[block.buildRequirements.length];
for(int i = 0; i < requirements.length; i++){ for(int i = 0; i < requirements.length; i++){
requirements[i] = new ItemStack(block.buildRequirements[i].item, 30 + block.buildRequirements[i].amount * 5); requirements[i] = new ItemStack(block.buildRequirements[i].item, 30 + block.buildRequirements[i].amount * 6);
} }
return new TechNode(block, requirements, children); return new TechNode(block, requirements, children);

View File

@@ -396,6 +396,17 @@ public class Control implements ApplicationListener{
music.update(); music.update();
loops.update(); loops.update();
if(Core.input.keyTap(Binding.fullscreen)){
boolean full = settings.getBool("fullscreen");
if(full){
graphics.setWindowedMode(graphics.getWidth(), graphics.getHeight());
}else{
graphics.setFullscreenMode(graphics.getDisplayMode());
}
settings.put("fullscreen", !full);
settings.save();
}
if(!state.is(State.menu)){ if(!state.is(State.menu)){
input.update(); input.update();

View File

@@ -225,6 +225,7 @@ public class UI implements ApplicationListener{
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
if(Core.scene == null) return;
Core.scene.resize(width, height); Core.scene.resize(width, height);
Events.fire(new ResizeEvent()); Events.fire(new ResizeEvent());
} }

View File

@@ -190,7 +190,9 @@ public class World implements ApplicationListener{
} }
} }
if(!headless){
addDarkness(tiles); addDarkness(tiles);
}
Entities.getAllGroups().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2)); Entities.getAllGroups().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2));
@@ -354,7 +356,7 @@ public class World implements ApplicationListener{
for(int x = 0; x < tiles.length; x++){ for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){ for(int y = 0; y < tiles[0].length; y++){
Tile tile = tiles[x][y]; Tile tile = tiles[x][y];
if(tile.block().solid && !tile.block().synthetic() && tile.block().fillsTile){ if(tile.isDarkened()){
dark[x][y] = darkIterations; dark[x][y] = darkIterations;
} }
} }
@@ -383,9 +385,21 @@ public class World implements ApplicationListener{
for(int x = 0; x < tiles.length; x++){ for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){ for(int y = 0; y < tiles[0].length; y++){
Tile tile = tiles[x][y]; Tile tile = tiles[x][y];
if(tile.block().solid && !tile.block().synthetic()){ if(tile.isDarkened()){
tiles[x][y].rotation(dark[x][y]); tiles[x][y].rotation(dark[x][y]);
} }
if(dark[x][y] == 4){
boolean full = true;
for(Point2 p : Geometry.d4){
int px = p.x + x, py = p.y + y;
if(Structs.inBounds(px, py, tiles) && !(tiles[px][py].isDarkened() && dark[px][py] == 4)){
full = false;
break;
}
}
if(full) tiles[x][y].rotation(5);
}
} }
} }
} }

View File

@@ -20,6 +20,10 @@ public interface HealthTrait{
default void onDeath(){ default void onDeath(){
} }
default boolean damaged(){
return health() < maxHealth() - 0.0001f;
}
default void damage(float amount){ default void damage(float amount){
health(health() - amount); health(health() - amount);
if(health() <= 0 && !isDead()){ if(health() <= 0 && !isDead()){

View File

@@ -16,6 +16,7 @@ import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.units.CommandCenter.*;
import io.anuke.mindustry.world.blocks.units.UnitFactory.*; import io.anuke.mindustry.world.blocks.units.UnitFactory.*;
import io.anuke.mindustry.world.meta.*; import io.anuke.mindustry.world.meta.*;
@@ -25,7 +26,6 @@ import static io.anuke.mindustry.Vars.*;
/** Base class for AI units. */ /** Base class for AI units. */
public abstract class BaseUnit extends Unit implements ShooterTrait{ public abstract class BaseUnit extends Unit implements ShooterTrait{
protected static int timerIndex = 0; protected static int timerIndex = 0;
protected static final int timerTarget = timerIndex++; protected static final int timerTarget = timerIndex++;
@@ -83,6 +83,22 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return world.tile(spawner); return world.tile(spawner);
} }
public boolean isCommanded(){
return world.indexer.getAllied(team, BlockFlag.comandCenter).size != 0 && world.indexer.getAllied(team, BlockFlag.comandCenter).first().entity instanceof CommandCenterEntity;
}
public UnitCommand getCommand(){
if(isCommanded()){
return world.indexer.getAllied(team, BlockFlag.comandCenter).first().<CommandCenterEntity>entity().command;
}
return null;
}
/**Called when a command is recieved from the command center.*/
public void onCommand(UnitCommand command){
}
/** Initialize the type and team of this unit. Only call once! */ /** Initialize the type and team of this unit. Only call once! */
public void init(UnitType type, Team team){ public void init(UnitType type, Team team){
if(this.type != null) throw new RuntimeException("This unit is already initialized!"); if(this.type != null) throw new RuntimeException("This unit is already initialized!");
@@ -303,6 +319,10 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
state.set(getStartState()); state.set(getStartState());
health(maxHealth()); health(maxHealth());
if(isCommanded()){
onCommand(getCommand());
}
} }
@Override @Override

View File

@@ -1,20 +1,19 @@
package io.anuke.mindustry.entities.type; package io.anuke.mindustry.entities.type;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.graphics.g2d.Fill; import io.anuke.arc.math.*;
import io.anuke.arc.math.Angles; import io.anuke.arc.math.geom.*;
import io.anuke.arc.math.Mathf; import io.anuke.arc.util.*;
import io.anuke.arc.math.geom.Vector2; import io.anuke.mindustry.entities.*;
import io.anuke.arc.util.Time; import io.anuke.mindustry.entities.bullet.*;
import io.anuke.arc.util.Tmp; import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.entities.Predict; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.world.*;
import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.world.meta.*;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.net.Net; import static io.anuke.mindustry.Vars.world;
import io.anuke.mindustry.world.meta.BlockFlag;
public abstract class FlyingUnit extends BaseUnit{ public abstract class FlyingUnit extends BaseUnit{
protected float[] weaponAngles = {0, 0}; protected float[] weaponAngles = {0, 0};
@@ -80,14 +79,40 @@ public abstract class FlyingUnit extends BaseUnit{
return; return;
} }
target = getClosestCore(); target = getSpawner();
}; if(target == null) target = getClosestCore();
}
if(target != null){ if(target != null){
circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f)); circle(80f + Mathf.randomSeed(id) * 120);
} }
} }
}; },
retreat = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(retarget()){
target = getSpawner();
Tile repair = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
if(repair != null && damaged()) FlyingUnit.this.target = repair.entity;
if(target == null) target = getClosestCore();
}
circle(targetHasFlag(BlockFlag.repair) ? 20f : 60f + Mathf.randomSeed(id) * 50, 0.65f * type.speed);
}
};;
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.patrol ? patrol :
null);
}
@Override @Override
public void move(float x, float y){ public void move(float x, float y){

View File

@@ -1,20 +1,18 @@
package io.anuke.mindustry.entities.type; package io.anuke.mindustry.entities.type;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Angles; import io.anuke.arc.math.*;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.*;
import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.util.*;
import io.anuke.arc.util.Time; import io.anuke.mindustry.*;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.Predict; import io.anuke.mindustry.entities.bullet.*;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.world.*;
import io.anuke.mindustry.type.Weapon; import io.anuke.mindustry.world.blocks.*;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
@@ -63,8 +61,25 @@ public abstract class GroundUnit extends BaseUnit{
} }
} }
} }
},
retreat = new UnitState(){
public void entered(){
target = null;
}
public void update(){
moveAwayFromCore();
}
}; };
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.patrol ? patrol :
null);
}
@Override @Override
public void interpolate(){ public void interpolate(){
super.interpolate(); super.interpolate();
@@ -182,9 +197,9 @@ public abstract class GroundUnit extends BaseUnit{
protected void patrol(){ protected void patrol(){
vec.trns(baseRotation, type.speed * Time.delta()); vec.trns(baseRotation, type.speed * Time.delta());
velocity.add(vec.x, vec.y); velocity.add(vec.x, vec.y);
vec.trns(baseRotation, type.hitsizeTile * 3); vec.trns(baseRotation, type.hitsizeTile * 5);
Tile tile = world.tileWorld(x + vec.x, y + vec.y); Tile tile = world.tileWorld(x + vec.x, y + vec.y);
if((tile == null || tile.solid() || tile.floor().drownTime > 0) || stuckTime > 10f){ if((tile == null || tile.solid() || tile.floor().drownTime > 0 || tile.floor().isLiquid) || stuckTime > 10f){
baseRotation += Mathf.sign(id % 2 - 0.5f) * Time.delta() * 3f; baseRotation += Mathf.sign(id % 2 - 0.5f) * Time.delta() * 3f;
} }
@@ -234,7 +249,7 @@ public abstract class GroundUnit extends BaseUnit{
Tile targetTile = world.pathfinder.getTargetTile(enemy, tile); Tile targetTile = world.pathfinder.getTargetTile(enemy, tile);
TileEntity core = getClosestCore(); TileEntity core = getClosestCore();
if(tile == targetTile || core == null || dst(core) < 90f) return; if(tile == targetTile || core == null || dst(core) < 120f) return;
velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta())); velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta()));
rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed); rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed);

View File

@@ -175,10 +175,6 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
} }
} }
public boolean damaged(){
return health < maxHealth() - 0.00001f;
}
public Tile getTile(){ public Tile getTile(){
return tile; return tile;
} }

View File

@@ -3,7 +3,7 @@ package io.anuke.mindustry.entities.type.base;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry; import io.anuke.arc.math.geom.Geometry;
import io.anuke.mindustry.entities.type.FlyingUnit; import io.anuke.mindustry.entities.type.FlyingUnit;
import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.mindustry.world.meta.BlockFlag;
@@ -33,6 +33,11 @@ public abstract class BaseDrone extends FlyingUnit{
} }
}; };
@Override
public void onCommand(UnitCommand command){
//do nothing, normal commands are not applicable here
}
@Override @Override
protected void updateRotation(){ protected void updateRotation(){
if(target != null && shouldRotate() && target.dst(this) < type.range){ if(target != null && shouldRotate() && target.dst(this) < type.range){

View File

@@ -0,0 +1,18 @@
package io.anuke.mindustry.entities.units;
import io.anuke.arc.*;
public enum UnitCommand{
attack, retreat, patrol;
private final String localized;
public static final UnitCommand[] all = values();
UnitCommand(){
localized = Core.bundle.get("command." + name());
}
public String localized(){
return localized;
}
}

View File

@@ -22,7 +22,7 @@ public class FloorRenderer implements Disposable{
private final static int chunksize = 64; private final static int chunksize = 64;
private Chunk[][] cache; private Chunk[][] cache;
private CacheBatch cbatch; private MultiCacheBatch cbatch;
private IntSet drawnLayerSet = new IntSet(); private IntSet drawnLayerSet = new IntSet();
private IntArray drawnLayers = new IntArray(); private IntArray drawnLayers = new IntArray();
private ObjectSet<CacheLayer> used = new ObjectSet<>(); private ObjectSet<CacheLayer> used = new ObjectSet<>();
@@ -185,7 +185,7 @@ public class FloorRenderer implements Disposable{
floor = tile.floor(); floor = tile.floor();
} }
if(tile.block().cacheLayer == layer && layer == CacheLayer.walls){ if(tile.block().cacheLayer == layer && layer == CacheLayer.walls && !(tile.isDarkened() && tile.rotation() >= 5)){
tile.block().draw(tile); tile.block().draw(tile);
}else if(floor.cacheLayer == layer && (world.isAccessible(tile.x, tile.y) || tile.block().cacheLayer != CacheLayer.walls || !tile.block().fillsTile)){ }else if(floor.cacheLayer == layer && (world.isAccessible(tile.x, tile.y) || tile.block().cacheLayer != CacheLayer.walls || !tile.block().fillsTile)){
floor.draw(tile); floor.draw(tile);
@@ -204,8 +204,7 @@ public class FloorRenderer implements Disposable{
int chunksx = Mathf.ceil((float)(world.width()) / chunksize), int chunksx = Mathf.ceil((float)(world.width()) / chunksize),
chunksy = Mathf.ceil((float)(world.height()) / chunksize); chunksy = Mathf.ceil((float)(world.height()) / chunksize);
cache = new Chunk[chunksx][chunksy]; cache = new Chunk[chunksx][chunksy];
SpriteCache sprites = new SpriteCache(world.width() * world.height() * 6, (world.width() / chunksize) * (world.height() / chunksize) * 2, false); cbatch = new MultiCacheBatch(chunksize * chunksize * 4);
cbatch = new CacheBatch(sprites);
Time.mark(); Time.mark();

View File

@@ -21,6 +21,7 @@ public enum Binding implements KeyBind{
zoom_hold(KeyCode.CONTROL_LEFT, "view"), zoom_hold(KeyCode.CONTROL_LEFT, "view"),
zoom(new Axis(KeyCode.SCROLL)), zoom(new Axis(KeyCode.SCROLL)),
menu(Core.app.getType() == ApplicationType.Android ? KeyCode.BACK : KeyCode.ESCAPE), menu(Core.app.getType() == ApplicationType.Android ? KeyCode.BACK : KeyCode.ESCAPE),
fullscreen(KeyCode.F11),
pause(KeyCode.SPACE), pause(KeyCode.SPACE),
minimap(KeyCode.M), minimap(KeyCode.M),
toggle_menus(KeyCode.C), toggle_menus(KeyCode.C),

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.io;
import io.anuke.annotations.Annotations.ReadClass; import io.anuke.annotations.Annotations.ReadClass;
import io.anuke.annotations.Annotations.WriteClass; import io.anuke.annotations.Annotations.WriteClass;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.arc.util.CommandHandler.*;
import io.anuke.mindustry.entities.Effects; import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.Effects.Effect; import io.anuke.mindustry.entities.Effects.Effect;
import io.anuke.mindustry.entities.Entities; import io.anuke.mindustry.entities.Entities;
@@ -11,6 +12,7 @@ import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.entities.traits.ShooterTrait; import io.anuke.mindustry.entities.traits.ShooterTrait;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Administration.TraceInfo; import io.anuke.mindustry.net.Administration.TraceInfo;
import io.anuke.mindustry.net.Packets.AdminAction; import io.anuke.mindustry.net.Packets.AdminAction;
@@ -177,6 +179,16 @@ public class TypeIO{
return Team.all[buffer.get()]; return Team.all[buffer.get()];
} }
@WriteClass(UnitCommand.class)
public static void writeUnitCommand(ByteBuffer buffer, UnitCommand reason){
buffer.put((byte)reason.ordinal());
}
@ReadClass(UnitCommand.class)
public static UnitCommand readUnitCommand(ByteBuffer buffer){
return UnitCommand.all[buffer.get()];
}
@WriteClass(AdminAction.class) @WriteClass(AdminAction.class)
public static void writeAction(ByteBuffer buffer, AdminAction reason){ public static void writeAction(ByteBuffer buffer, AdminAction reason){
buffer.put((byte)reason.ordinal()); buffer.put((byte)reason.ordinal());

View File

@@ -227,8 +227,8 @@ public class Maps implements Disposable{
int index = 0; int index = 0;
for(Block block : new Block[]{Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreTitanium, Blocks.oreThorium}){ for(Block block : new Block[]{Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreTitanium, Blocks.oreThorium}){
OreFilter filter = new OreFilter(); OreFilter filter = new OreFilter();
filter.threshold += index ++ * 0.019f; filter.threshold += index ++ * 0.018f;
filter.scl += index/2f; filter.scl += index/2.1f;
filter.ore = block; filter.ore = block;
filters.add(filter); filters.add(filter);
} }

View File

@@ -8,7 +8,7 @@ import static io.anuke.mindustry.maps.filters.FilterOption.BlockOption;
import static io.anuke.mindustry.maps.filters.FilterOption.oresOnly; import static io.anuke.mindustry.maps.filters.FilterOption.oresOnly;
public class OreFilter extends GenerateFilter{ public class OreFilter extends GenerateFilter{
public float scl = 23, threshold = 0.811f, octaves = 2f, falloff = 0.3f; public float scl = 23, threshold = 0.81f, octaves = 2f, falloff = 0.3f;
public Block ore = Blocks.oreCopper; public Block ore = Blocks.oreCopper;
{ {

View File

@@ -97,7 +97,7 @@ public class CrashSender{
ex(() -> value.addChild("server", new JsonValue(fs))); ex(() -> value.addChild("server", new JsonValue(fs)));
ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size()))); ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size())));
ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name()))); ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name())));
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name")))); ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name") + "x" + (OS.is64Bit ? "64" : "32"))));
ex(() -> value.addChild("trace", new JsonValue(parseException(exception)))); ex(() -> value.addChild("trace", new JsonValue(parseException(exception))));
boolean[] sent = {false}; boolean[] sent = {false};

View File

@@ -198,7 +198,7 @@ public class SettingsMenuDialog extends SettingsDialog{
} }
return s + "%"; return s + "%";
}); });
graphics.sliderPref("fpscap", 241, 5, 241, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s))); graphics.sliderPref("fpscap", 240, 5, 245, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%"); graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
if(!mobile){ if(!mobile){
@@ -207,7 +207,7 @@ public class SettingsMenuDialog extends SettingsDialog{
if(b){ if(b){
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode()); Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
}else{ }else{
Core.graphics.setWindowedMode(600, 480); Core.graphics.setWindowedMode(Core.graphics.getWidth(), Core.graphics.getHeight());
} }
}); });

View File

@@ -1,26 +1,22 @@
package io.anuke.mindustry.ui.fragments; package io.anuke.mindustry.ui.fragments;
import io.anuke.arc.Core; import io.anuke.arc.*;
import io.anuke.arc.Events; import io.anuke.arc.collection.*;
import io.anuke.arc.collection.Array; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Color; import io.anuke.arc.input.*;
import io.anuke.arc.input.KeyCode; import io.anuke.arc.math.geom.*;
import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.scene.*;
import io.anuke.arc.scene.Group; import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.event.Touchable; import io.anuke.arc.scene.style.*;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.input.Binding; import io.anuke.mindustry.input.*;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.Block.Icon; import io.anuke.mindustry.world.Block.*;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -151,8 +147,7 @@ public class PlacementFragment extends Fragment{
button.update(() -> { //color unplacable things gray button.update(() -> { //color unplacable things gray
TileEntity core = player.getClosestCore(); TileEntity core = player.getClosestCore();
Color color = block.buildVisibility == Blocks.padVisible && !block.buildVisibility.get() ? Pal.noplace : Color color = state.rules.infiniteResources || (core != null && (core.items.has(block.buildRequirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.WHITE : Color.GRAY;
state.rules.infiniteResources || (core != null && (core.items.has(block.buildRequirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.WHITE : Color.GRAY;
button.forEach(elem -> elem.setColor(color)); button.forEach(elem -> elem.setColor(color));
button.setChecked(input.block == block); button.setChecked(input.block == block);
}); });
@@ -204,10 +199,6 @@ public class PlacementFragment extends Fragment{
Events.fire(new BlockInfoEvent()); Events.fire(new BlockInfoEvent());
}).size(8 * 5).padTop(-5).padRight(-5).right().grow().name("blockinfo"); }).size(8 * 5).padTop(-5).padRight(-5).right().grow().name("blockinfo");
} }
if(lastDisplay.buildVisibility == Blocks.padVisible && !lastDisplay.buildVisibility.get()){
header.row();
header.add("$attackpvponly").width(230f).wrap().colspan(3).left();
}
}).growX().left(); }).growX().left();
topTable.row(); topTable.row();
//add requirement table //add requirement table
@@ -305,7 +296,7 @@ public class PlacementFragment extends Fragment{
Array<Block> getByCategory(Category cat){ Array<Block> getByCategory(Category cat){
returnArray.clear(); returnArray.clear();
for(Block block : content.blocks()){ for(Block block : content.blocks()){
if(block.buildCategory == cat && (block.isVisible() || block.buildVisibility == Blocks.padVisible)){ if(block.buildCategory == cat && block.isVisible()){
returnArray.add(block); returnArray.add(block);
} }
} }

View File

@@ -104,6 +104,10 @@ public class Tile implements Position, TargetTrait{
return block().offset() + worldy(); return block().offset() + worldy();
} }
public boolean isDarkened(){
return block().solid && !block().synthetic() && block().fillsTile;
}
public Floor floor(){ public Floor floor(){
return floor; return floor;
} }

View File

@@ -10,6 +10,7 @@ import static io.anuke.mindustry.Vars.*;
public class StaticWall extends Rock{ public class StaticWall extends Rock{
TextureRegion large; TextureRegion large;
TextureRegion[][] split;
public StaticWall(String name){ public StaticWall(String name){
super(name); super(name);
@@ -25,9 +26,7 @@ public class StaticWall extends Rock{
int ry = tile.y / 2 * 2; int ry = tile.y / 2 * 2;
if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Pos.get(rx, ry)) < 0.5){ if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Pos.get(rx, ry)) < 0.5){
if(rx == tile.x && ry == tile.y){ Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy());
Draw.rect(large, tile.worldx() + tilesize / 2f, tile.worldy() + tilesize / 2f);
}
}else if(variants > 0){ }else if(variants > 0){
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy()); Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
}else{ }else{
@@ -39,6 +38,7 @@ public class StaticWall extends Rock{
public void load(){ public void load(){
super.load(); super.load();
large = Core.atlas.find(name + "-large"); large = Core.atlas.find(name + "-large");
split = large.split(32, 32);
} }
boolean eq(int rx, int ry){ boolean eq(int rx, int ry){

View File

@@ -20,6 +20,7 @@ import static io.anuke.mindustry.Vars.world;
public class Door extends Wall{ public class Door extends Wall{
protected final Rectangle rect = new Rectangle(); protected final Rectangle rect = new Rectangle();
protected int timerToggle = timers++;
protected Effect openfx = Fx.dooropen; protected Effect openfx = Fx.dooropen;
protected Effect closefx = Fx.doorclose; protected Effect closefx = Fx.doorclose;
@@ -81,7 +82,7 @@ public class Door extends Wall{
public void tapped(Tile tile, Player player){ public void tapped(Tile tile, Player player){
DoorEntity entity = tile.entity(); DoorEntity entity = tile.entity();
if(Units.anyEntities(tile) && entity.open){ if((Units.anyEntities(tile) && entity.open) || !tile.entity.timer.get(timerToggle, 30f)){
return; return;
} }

View File

@@ -0,0 +1,129 @@
package io.anuke.mindustry.world.blocks.units;
import io.anuke.annotations.Annotations.*;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.Effects.*;
import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.meta.*;
import java.io.*;
import static io.anuke.mindustry.Vars.*;
public class CommandCenter extends Block{
protected TextureRegion[] commandRegions = new TextureRegion[UnitCommand.all.length];
protected Color topColor = Pal.command;
protected Color bottomColor = Color.valueOf("5e5e5e");
protected Effect effect = Fx.commandSend;
public CommandCenter(String name){
super(name);
flags = EnumSet.of(BlockFlag.comandCenter);
destructible = true;
solid = true;
configurable = true;
}
@Override
public void placed(Tile tile){
super.placed(tile);
ObjectSet<Tile> set = world.indexer.getAllied(tile.getTeam(), BlockFlag.comandCenter);
if(set.size > 0){
CommandCenterEntity entity = tile.entity();
CommandCenterEntity oe = set.first().entity();
entity.command = oe.command;
}
}
@Override
public void load(){
super.load();
for(UnitCommand cmd : UnitCommand.all){
commandRegions[cmd.ordinal()] = Core.atlas.find("icon-command-" + cmd.name() + "-small");
}
}
@Override
public void draw(Tile tile){
CommandCenterEntity entity = tile.entity();
super.draw(tile);
float size = iconsizesmall/4f;
Draw.color(bottomColor);
Draw.rect(commandRegions[entity.command.ordinal()], tile.drawx(), tile.drawy() - 1, size, size);
Draw.color(topColor);
Draw.rect(commandRegions[entity.command.ordinal()], tile.drawx(), tile.drawy(), size, size);
Draw.color();
}
@Override
public void buildTable(Tile tile, Table table){
CommandCenterEntity entity = tile.entity();
ButtonGroup<ImageButton> group = new ButtonGroup<>();
Table buttons = new Table();
for(UnitCommand cmd : UnitCommand.all){
buttons.addImageButton("icon-command-" + cmd.name() + "-small", "clear-toggle-trans", iconsizesmall, () -> Call.onCommandCenterSet(player, tile, cmd))
.size(44).group(group).update(b -> b.setChecked(entity.command == cmd));
}
table.add(buttons);
table.row();
table.label(() -> entity.command.localized()).style("outline").center().growX().get().setAlignment(Align.center);
}
@Remote(called = Loc.server, forward = true, targets = Loc.both)
public static void onCommandCenterSet(Player player, Tile tile, UnitCommand command){
Effects.effect(((CommandCenter)tile.block()).effect, tile);
for(Tile center : world.indexer.getAllied(tile.getTeam(), BlockFlag.comandCenter)){
if(center.block() instanceof CommandCenter){
CommandCenterEntity entity = center.entity();
entity.command = command;
}
}
Team team = (player == null ? tile.getTeam() : player.getTeam());
for(BaseUnit unit : unitGroups[team.ordinal()].all()){
unit.onCommand(command);
}
}
@Override
public TileEntity newEntity(){
return new CommandCenterEntity();
}
public class CommandCenterEntity extends TileEntity{
public UnitCommand command = UnitCommand.attack;
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
stream.writeByte(command.ordinal());
}
@Override
public void read(DataInput stream, byte version) throws IOException{
super.read(stream, version);
command = UnitCommand.all[stream.readByte()];
}
}
}

View File

@@ -7,6 +7,8 @@ public enum BlockFlag{
producer(Float.MAX_VALUE), producer(Float.MAX_VALUE),
/** A turret. */ /** A turret. */
turret(Float.MAX_VALUE), turret(Float.MAX_VALUE),
/** Only the command center block.*/
comandCenter(Float.MAX_VALUE),
/** Repair point. */ /** Repair point. */
repair(Float.MAX_VALUE); repair(Float.MAX_VALUE);

View File

@@ -9,6 +9,7 @@ sourceSets.main.java.srcDirs = [ "src/" ]
project.ext.mainClassName = "io.anuke.mindustry.desktop.DesktopLauncher" project.ext.mainClassName = "io.anuke.mindustry.desktop.DesktopLauncher"
project.ext.assetsDir = new File("../core/assets") project.ext.assetsDir = new File("../core/assets")
def enableTemplates = true
def JDK_DIR = "$System.env.PACKR_DIR" def JDK_DIR = "$System.env.PACKR_DIR"
def ICON_DIR = new File("core/assets/icons/icon.icns") def ICON_DIR = new File("core/assets/icons/icon.icns")
@@ -49,7 +50,6 @@ task dist(type: Jar, dependsOn: classes){
PackrConfig.Platform.values().each{ platform -> PackrConfig.Platform.values().each{ platform ->
task "packr${platform.toString()}"{ task "packr${platform.toString()}"{
def platformName = platform.toString().replace('64', '').replace('32', '').replace('MacOS', 'Mac')
dependsOn dist dependsOn dist
@@ -64,7 +64,7 @@ PackrConfig.Platform.values().each{ platform ->
delete "build/packr/output/" delete "build/packr/output/"
} }
if(platform == PackrConfig.Platform.Windows32 || platform == PackrConfig.Platform.Windows64){ if(enableTemplates && (platform == PackrConfig.Platform.Windows64)){
copy{ copy{
into "build/packr/output" into "build/packr/output"
from "${JDK_DIR}/templates/${platform.toString().toLowerCase()}" from "${JDK_DIR}/templates/${platform.toString().toLowerCase()}"
@@ -99,7 +99,7 @@ PackrConfig.Platform.values().each{ platform ->
new Packr().pack(config) new Packr().pack(config)
if(platform == PackrConfig.Platform.Linux64){ if(platform != PackrConfig.Platform.MacOS){
copy{ copy{
into "build/packr/output/jre/" into "build/packr/output/jre/"
from "build/packr/output/desktop.jar" from "build/packr/output/desktop.jar"
@@ -114,15 +114,23 @@ PackrConfig.Platform.values().each{ platform ->
if(platform == PackrConfig.Platform.MacOS){ if(platform == PackrConfig.Platform.MacOS){
copy{ copy{
into "build/packr/${appName}.app/Contents/" into "build/packr/output/${appName}.app/Contents/"
from "build/packr/Contents/" from "build/packr/output/Contents/"
} }
delete{ delete{
delete "build/packr/Contents/" delete "build/packr/output/Contents/"
} }
} }
} }
if((platform == PackrConfig.Platform.Windows64 || platform == PackrConfig.Platform.Windows32)){
copy{
from "build/packr/output/jre/bin/msvcr100.dll"
into "build/packr/output/"
rename("msvcr100.dll", "MSVCR100.dll")
}
}
} }
task "zip${platform.toString()}"(type: Zip){ task "zip${platform.toString()}"(type: Zip){

View File

@@ -12,8 +12,8 @@ public class DesktopLauncher{
try{ try{
Platform.instance = new DesktopPlatform(arg); Platform.instance = new DesktopPlatform(arg);
Net.setClientProvider(new MClient()); Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new MServer()); Net.setServerProvider(new ArcNetServer());
new SdlApplication(new Mindustry(), new SdlConfig(){{ new SdlApplication(new Mindustry(), new SdlConfig(){{
title = "Mindustry"; title = "Mindustry";

View File

@@ -26,8 +26,8 @@ public class IOSLauncher extends IOSApplication.Delegate{
@Override @Override
protected IOSApplication createApplication(){ protected IOSApplication createApplication(){
Net.setClientProvider(new MClient()); Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new MServer()); Net.setServerProvider(new ArcNetServer());
if(UIDevice.getCurrentDevice().getUserInterfaceIdiom() == UIUserInterfaceIdiom.Pad){ if(UIDevice.getCurrentDevice().getUserInterfaceIdiom() == UIUserInterfaceIdiom.Pad){
UnitScl.dp.addition = 0.5f; UnitScl.dp.addition = 0.5f;

View File

@@ -0,0 +1,189 @@
package io.anuke.mindustry.net;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.function.*;
import io.anuke.arc.net.*;
import io.anuke.arc.util.async.*;
import io.anuke.arc.util.pooling.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import static io.anuke.mindustry.Vars.*;
public class ArcNetClient implements ClientProvider{
final Client client;
final Supplier<DatagramPacket> packetSupplier = () -> new DatagramPacket(new byte[256], 256);
public ArcNetClient(){
client = new Client(8192, 4096, new PacketSerializer());
client.setDiscoveryPacket(packetSupplier);
NetListener listener = new NetListener(){
@Override
public void connected(Connection connection){
Connect c = new Connect();
c.addressTCP = connection.getRemoteAddressTCP().getAddress().getHostAddress();
c.id = connection.getID();
if(connection.getRemoteAddressTCP() != null) c.addressTCP = connection.getRemoteAddressTCP().toString();
Core.app.post(() -> Net.handleClientReceived(c));
}
@Override
public void disconnected(Connection connection){
if(connection.getLastProtocolError() != null){
netClient.setQuiet();
}
Disconnect c = new Disconnect();
Core.app.post(() -> Net.handleClientReceived(c));
}
@Override
public void received(Connection connection, Object object){
if(object instanceof FrameworkMessage) return;
Core.app.post(() -> {
try{
Net.handleClientReceived(object);
}catch(Exception e){
handleException(e);
}
});
}
};
client.addListener(listener);
}
private static boolean isLocal(InetAddress addr){
if(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) return true;
try{
return NetworkInterface.getByInetAddress(addr) != null;
}catch(Exception e){
return false;
}
}
@Override
public void connect(String ip, int port, Runnable success){
Threads.daemon(() -> {
try{
//just in case
client.stop();
Threads.daemon("Net Client", () -> {
try{
client.run();
}catch(Exception e){
if(!(e instanceof ClosedSelectorException)) handleException(e);
}
});
client.connect(5000, ip, port, port);
success.run();
}catch(Exception e){
handleException(e);
}
});
}
@Override
public void disconnect(){
client.close();
}
@Override
public void send(Object object, SendMode mode){
try{
if(mode == SendMode.tcp){
client.sendTCP(object);
}else{
client.sendUDP(object);
}
//sending things can cause an under/overflow, catch it and disconnect instead of crashing
}catch(BufferOverflowException | BufferUnderflowException e){
Net.showError(e);
}
Pools.free(object);
}
@Override
public void updatePing(){
client.updateReturnTripTime();
}
@Override
public int getPing(){
return client.getReturnTripTime();
}
@Override
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> invalid){
Threads.daemon(() -> {
try{
DatagramSocket socket = new DatagramSocket();
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
socket.setSoTimeout(2000);
DatagramPacket packet = packetSupplier.get();
socket.receive(packet);
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
Core.app.post(() -> valid.accept(host));
}catch(Exception e){
Core.app.post(() -> invalid.accept(e));
}
});
}
@Override
public void discover(Consumer<Host> callback, Runnable done){
Array<InetAddress> foundAddresses = new Array<>();
client.discoverHosts(port, multicastGroup, multicastPort, 3000, packet -> {
Core.app.post(() -> {
try{
if(foundAddresses.contains(address -> address.equals(packet.getAddress()) || (isLocal(address) && isLocal(packet.getAddress())))){
return;
}
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
callback.accept(host);
foundAddresses.add(packet.getAddress());
}catch(Exception e){
//don't crash when there's an error pinging a a server or parsing data
e.printStackTrace();
}
});
}, () -> Core.app.post(done));
}
@Override
public void dispose(){
try{
client.dispose();
}catch(IOException e){
throw new RuntimeException(e);
}
}
private void handleException(Exception e){
if(e instanceof ArcNetException){
Core.app.post(() -> Net.showError(new IOException("mismatch")));
}else{
Core.app.post(() -> Net.showError(e));
}
}
}

View File

@@ -0,0 +1,175 @@
package io.anuke.mindustry.net;
import io.anuke.arc.*;
import io.anuke.arc.net.*;
import io.anuke.arc.util.*;
import io.anuke.arc.util.async.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.concurrent.*;
import static io.anuke.mindustry.Vars.*;
public class ArcNetServer implements ServerProvider{
final Server server;
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
Thread serverThread;
public ArcNetServer(){
server = new Server(4096 * 2, 4096, new PacketSerializer());
server.setMulticast(multicastGroup, multicastPort);
server.setDiscoveryHandler((address, handler) -> {
ByteBuffer buffer = NetworkIO.writeServerData();
buffer.position(0);
handler.respond(buffer);
});
NetListener listener = new NetListener(){
@Override
public void connected(Connection connection){
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
ArcConnection kn = new ArcConnection(ip, connection);
Connect c = new Connect();
c.id = kn.id;
c.addressTCP = ip;
Log.debug("&bRecieved connection: {0}", c.addressTCP);
connections.add(kn);
Core.app.post(() -> Net.handleServerReceived(kn.id, c));
}
@Override
public void disconnected(Connection connection){
ArcConnection k = getByKryoID(connection.getID());
if(k == null) return;
Disconnect c = new Disconnect();
c.id = k.id;
Core.app.post(() -> {
Net.handleServerReceived(k.id, c);
connections.remove(k);
});
}
@Override
public void received(Connection connection, Object object){
ArcConnection k = getByKryoID(connection.getID());
if(object instanceof FrameworkMessage || k == null) return;
Core.app.post(() -> {
try{
Net.handleServerReceived(k.id, object);
}catch(RuntimeException e){
if(e.getCause() instanceof ValidateException){
ValidateException v = (ValidateException)e.getCause();
Log.err("Validation failed: {0} ({1})", v.player.name, v.getMessage());
}else{
e.printStackTrace();
}
}catch(Exception e){
e.printStackTrace();
}
});
}
};
server.addListener(listener);
}
@Override
public Iterable<ArcConnection> getConnections(){
return connections;
}
@Override
public ArcConnection getByID(int id){
for(int i = 0; i < connections.size(); i++){
ArcConnection con = connections.get(i);
if(con.id == id){
return con;
}
}
return null;
}
@Override
public void host(int port) throws IOException{
connections.clear();
server.bind(port, port);
serverThread = new Thread(() -> {
try{
server.run();
}catch(Throwable e){
if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e);
}
}, "Net Server");
serverThread.setDaemon(true);
serverThread.start();
}
@Override
public void close(){
connections.clear();
Threads.daemon(server::stop);
}
ArcConnection getByKryoID(int id){
for(int i = 0; i < connections.size(); i++){
ArcConnection con = connections.get(i);
if(con.connection != null && con.connection.getID() == id){
return con;
}
}
return null;
}
class ArcConnection extends NetConnection{
public final Connection connection;
public ArcConnection(String address, Connection connection){
super(address);
this.connection = connection;
}
@Override
public boolean isConnected(){
return connection.isConnected();
}
@Override
public void send(Object object, SendMode mode){
try{
if(mode == SendMode.tcp){
connection.sendTCP(object);
}else{
connection.sendUDP(object);
}
}catch(Exception e){
Log.err(e);
Log.info("Error sending packet. Disconnecting invalid client!");
connection.close();
ArcConnection k = getByKryoID(connection.getID());
if(k != null) connections.remove(k);
}
}
@Override
public void close(){
if(connection.isConnected()) connection.close();
}
}
}

View File

@@ -1,179 +0,0 @@
package io.anuke.mindustry.net;
import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.arc.util.async.*;
import io.anuke.arc.util.pooling.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*;
import io.anuke.mnet.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import static io.anuke.mindustry.Vars.*;
public class MClient implements ClientProvider, ApplicationListener{
MSocket socket;
public MClient(){
Events.on(AppLoadEvent.class, event -> Core.app.addListener(this));
}
public void connect(String ip, int port, Runnable success) throws IOException{
socket = new MSocket(InetAddress.getByName(ip), port, PacketSerializer::new);
socket.addDcListener((sock, reason) -> Core.app.post(() -> Net.handleClientReceived(new Disconnect())));
socket.connectAsync(null, 2000, response -> {
if(response.getType() == ResponseType.ACCEPTED){
Core.app.post(() -> {
success.run();
Connect c = new Connect();
c.addressTCP = ip;
Net.handleClientReceived(c);
});
}else if(response.getType() == ResponseType.WRONG_STATE){
Core.app.post(() -> Net.showError(new IOException("alreadyconnected")));
}else{
Core.app.post(() -> Net.showError(new IOException("connection refused")));
}
});
}
@Override
public void update(){
if(socket == null) return;
socket.update((sock, object) -> {
try{
Net.handleClientReceived(object);
}catch(Exception e){
Net.showError(e);
netClient.disconnectQuietly();
}
});
}
@Override
public void updatePing(){
}
@Override
public void dispose(){
disconnect();
}
public void send(Object object, SendMode mode){
if(mode == SendMode.tcp){
socket.send(object);
}else{
socket.sendUnreliable(object);
}
Pools.free(object);
}
public int getPing(){
return socket == null ? 0 : (int)socket.getPing();
}
public void disconnect(){
if(socket != null) socket.close();
}
public void discover(Consumer<Host> callback, Runnable done){
Threads.daemon(() -> {
byte[] bytes = new byte[512];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
ArrayList<InetAddress> foundAddresses = new ArrayList<>();
try(DatagramSocket socket = new DatagramSocket()){
broadcast(port, socket);
socket.setSoTimeout(4000);
outer:
while(true){
try{
socket.receive(packet);
}catch(SocketTimeoutException ex){
done.run();
return;
}
buffer.position(0);
InetAddress address = ((InetSocketAddress)packet.getSocketAddress()).getAddress();
for(InetAddress other : foundAddresses){
if(other.equals(address) || (isLocal(other) && isLocal(address))){
continue outer;
}
}
Host host = NetworkIO.readServerData(address.getHostName(), buffer);
callback.accept(host);
foundAddresses.add(address);
}
}catch(IOException ex){
done.run();
}
});
}
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
Threads.daemon(() -> {
try{
DatagramPacket packet = new DatagramPacket(new byte[512], 512);
DatagramSocket socket = new DatagramSocket();
socket.send(new DatagramPacket(new byte[]{-2}, 1, InetAddress.getByName(address), port));
socket.setSoTimeout(4000);
socket.receive(packet);
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
Core.app.post(() -> valid.accept(host));
}catch(Exception e){
Core.app.post(() -> failed.accept(e));
}
});
}
private void broadcast (int udpPort, DatagramSocket socket) throws IOException{
byte[] data = {-2};
for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())){
for (InetAddress address : Collections.list(iface.getInetAddresses())){
byte[] ip = address.getAddress(); //255.255.255.255
try{
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort));
}catch (Exception ignored){}
ip[3] = -1; //255.255.255.0
try{
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort));
}catch (Exception ignored){}
ip[2] = -1; //255.255.0.0
try{
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), udpPort));
}catch (Exception ignored){}
}
}
}
private boolean isLocal(InetAddress addr) {
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) return true;
try {
return NetworkInterface.getByInetAddress(addr) != null;
} catch (Exception e) {
return false;
}
}
}

View File

@@ -1,127 +0,0 @@
package io.anuke.mindustry.net;
import io.anuke.arc.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*;
import io.anuke.mnet.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.concurrent.*;
public class MServer implements ServerProvider, ApplicationListener{
final CopyOnWriteArrayList<MConnectionImpl> connections = new CopyOnWriteArrayList<>();
MServerSocket socket;
public MServer(){
Events.on(AppLoadEvent.class, event -> Core.app.addListener(this));
}
@Override
public void update(){
if(socket == null) return;
socket.update();
for(MSocket socket : socket.getSockets()){
MConnectionImpl c = socket.getUserData();
socket.update((s, msg) -> Core.app.post(() -> {
try{
Net.handleServerReceived(c.id, msg);
}catch(Exception e){
e.printStackTrace();
}
}));
}
}
@Override
public void host(int port) throws IOException{
close();
socket = new MServerSocket(port, con -> {
MSocket sock = con.accept(null);
MConnectionImpl kn = new MConnectionImpl(sock);
sock.setUserData(kn);
String ip = sock.getRemoteAddress().getHostAddress();
Connect c = new Connect();
c.id = kn.id;
c.addressTCP = ip;
Log.info("&bRecieved connection: {0} / {1}", c.id, c.addressTCP);
connections.add(kn);
Core.app.post(() -> Net.handleServerReceived(kn.id, c));
sock.addDcListener((socket, message) -> {
Log.info("&bLost connection {0}. Reason: {1}", kn.id, message);
Disconnect dc = new Disconnect();
dc.id = kn.id;
Core.app.post(() -> {
Net.handleServerReceived(kn.id, dc);
connections.remove(kn);
});
});
}, PacketSerializer::new, () -> {
ByteBuffer buf = NetworkIO.writeServerData();
byte[] bytes = buf.array();
return new DatagramPacket(bytes, bytes.length);
});
connections.clear();
}
@Override
public void close(){
if(socket != null){
socket.close();
socket = null;
}
}
@Override
public Iterable<? extends NetConnection> getConnections(){
return connections;
}
@Override
public MConnectionImpl getByID(int id){
for(MConnectionImpl n : connections){
if(n.id == id){
return n;
}
}
return null;
}
class MConnectionImpl extends NetConnection{
private final MSocket sock; //sock.
public MConnectionImpl(MSocket con){
super(con.getRemoteAddress().getHostAddress());
this.sock = con;
}
@Override
public void send(Object object, SendMode mode){
if(mode == SendMode.tcp){
sock.send(object);
}else{
sock.sendUnreliable(object);
}
}
@Override
public void close(){
sock.close();
}
}
}

View File

@@ -1,22 +1,21 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
import io.anuke.arc.net.*;
import io.anuke.arc.net.FrameworkMessage.*;
import io.anuke.arc.util.pooling.*; import io.anuke.arc.util.pooling.*;
import io.anuke.mnet.*;
import java.nio.*; import java.nio.*;
import java.util.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class PacketSerializer implements MSerializer{ public class PacketSerializer implements NetSerializer{
private ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024 * 8);
@Override
public void write(ByteBuffer byteBuffer, Object o){ public void write(ByteBuffer byteBuffer, Object o){
if(o == null){ if(o instanceof FrameworkMessage){
byteBuffer.put((byte)-1); byteBuffer.put((byte)-2); //code for framework message
return; writeFramework(byteBuffer, (FrameworkMessage)o);
} }else{
if(!(o instanceof Packet)) if(!(o instanceof Packet))
throw new RuntimeException("All sent objects must implement be Packets! Class: " + o.getClass()); throw new RuntimeException("All sent objects must implement be Packets! Class: " + o.getClass());
byte id = Registrator.getID(o.getClass()); byte id = Registrator.getID(o.getClass());
@@ -25,56 +24,64 @@ public class PacketSerializer implements MSerializer{
byteBuffer.put(id); byteBuffer.put(id);
((Packet)o).write(byteBuffer); ((Packet)o).write(byteBuffer);
} }
}
@Override
public Object read(ByteBuffer byteBuffer){ public Object read(ByteBuffer byteBuffer){
byte id = byteBuffer.get(); byte id = byteBuffer.get();
if(id == -1){ if(id == -2){
return null; return readFramework(byteBuffer);
} }else{
Packet packet = Pools.obtain((Class<Packet>)Registrator.getByID(id).type, (Supplier<Packet>)Registrator.getByID(id).constructor); Packet packet = Pools.obtain((Class<Packet>)Registrator.getByID(id).type, (Supplier<Packet>)Registrator.getByID(id).constructor);
packet.read(byteBuffer); packet.read(byteBuffer);
return packet; return packet;
} }
@Override
public byte[] serialize(Object o){
buffer.position(0);
write(buffer, o);
return Arrays.copyOfRange(buffer.array(), 0, buffer.position());
} }
@Override
public byte[] serialize(Object o, int offset){ public static void writeFramework(ByteBuffer buffer, FrameworkMessage message){
buffer.position(0); if(message instanceof Ping){
write(buffer, o); Ping p = (Ping)message;
int length = buffer.position(); buffer.put((byte)0);
byte[] bytes = new byte[length + offset]; buffer.putInt(p.id);
System.arraycopy(buffer.array(), 0, bytes, offset, length); buffer.put(p.isReply ? 1 : (byte)0);
return bytes; }else if(message instanceof DiscoverHost){
buffer.put((byte)1);
}else if(message instanceof KeepAlive){
buffer.put((byte)2);
}else if(message instanceof RegisterUDP){
RegisterUDP p = (RegisterUDP)message;
buffer.put((byte)3);
buffer.putInt(p.connectionID);
}else if(message instanceof RegisterTCP){
RegisterTCP p = (RegisterTCP)message;
buffer.put((byte)4);
buffer.putInt(p.connectionID);
}
} }
@Override public static FrameworkMessage readFramework(ByteBuffer buffer){
public int serialize(Object o, byte[] bytes, int offset){ byte id = buffer.get();
buffer.position(0);
write(buffer, o);
int length = buffer.position();
System.arraycopy(buffer.array(), 0, bytes, offset, length);
return length;
}
@Override if(id == 0){
public Object deserialize(byte[] bytes){ Ping p = new Ping();
buffer.position(0); p.id = buffer.getInt();
buffer.put(bytes); p.isReply = buffer.get() == 1;
buffer.position(0); return p;
return read(buffer); }else if(id == 1){
return new DiscoverHost();
}else if(id == 2){
return new KeepAlive();
}else if(id == 3){
RegisterUDP p = new RegisterUDP();
p.connectionID = buffer.getInt();
return p;
}else if(id == 4){
RegisterTCP p = new RegisterTCP();
p.connectionID = buffer.getInt();
return p;
}else{
throw new RuntimeException("Unknown framework message!");
} }
@Override
public Object deserialize(byte[] bytes, int offset, int length){
buffer.position(0);
buffer.put(bytes, offset, length);
buffer.position(0);
return read(buffer);
} }
} }

View File

@@ -8,8 +8,8 @@ public class ServerLauncher{
public static void main(String[] args){ public static void main(String[] args){
try{ try{
Net.setClientProvider(new MClient()); Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new MServer()); Net.setServerProvider(new ArcNetServer());
new HeadlessApplication(new MindustryServer(args), null, throwable -> CrashSender.send(throwable, f -> {})); new HeadlessApplication(new MindustryServer(args), null, throwable -> CrashSender.send(throwable, f -> {}));
}catch(Throwable t){ }catch(Throwable t){
CrashSender.send(t, f -> {}); CrashSender.send(t, f -> {});

View File

@@ -26,7 +26,7 @@ if(!hasProperty("release")){
use(':Arc:extensions') use(':Arc:extensions')
use(':Arc:extensions:freetype') use(':Arc:extensions:freetype')
use(':Arc:extensions:recorder') use(':Arc:extensions:recorder')
use(':Arc:extensions:mnet') use(':Arc:extensions:arcnet')
use(':Arc:extensions:packer') use(':Arc:extensions:packer')
use(':Arc:backends') use(':Arc:backends')
use(':Arc:backends:backend-sdl') use(':Arc:backends:backend-sdl')

View File

@@ -1,112 +0,0 @@
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.world.Tile;
import org.junit.jupiter.api.*;
import static io.anuke.mindustry.Vars.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WorldTests{
static Tile[][] tiles;
@BeforeAll
static void launchApplication(){
ApplicationTests.launchApplication();
world.createTiles(10, 10);
tiles = world.getTiles();
}
@BeforeEach
void resetWorld(){
Time.setDeltaProvider(() -> 1f);
logic.reset();
state.set(State.menu);
}
@Test
void addDarknessAllSolidMaxDarkness(){
fillWith(Blocks.rocks.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
assertEquals(4, tiles[x][y].rotation());
}
}
}
@Test
void addDarknessAllSyntethicNoDarkness(){
fillWith(Blocks.copperWall.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
assertEquals(0, tiles[x][y].rotation());
}
}
}
@Test
void addDarknessAllNotSolidNoDarkness(){
fillWith(Blocks.air.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
assertEquals(0, tiles[x][y].rotation());
}
}
}
@Test
void addDarknessAllNotFilledNoDarkness(){
fillWith(Blocks.cliffs.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
assertEquals(0, tiles[x][y].rotation());
}
}
}
private static void fillWith(short tileID){
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y] = new Tile(x, y, (short)0, (short)0, tileID);
}
}
}
@Test
void addDarknessOneNotSolidMiddleNoDarkness(){
fillWith(Blocks.rocks.id);
tiles[5][5] = new Tile(5, 5, (byte)0, (byte)0, Blocks.copperWall.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
byte darkness = tiles[x][y].rotation();
int distance = Math.abs(x - 5) + Math.abs(y - 5);
assertEquals(Math.min(Math.max(distance - 1, 0), 4), darkness);
}
}
}
@Test
void addDarknessOneNotSolidCornerNoDarkness(){
fillWith(Blocks.rocks.id);
tiles[7][7] = new Tile(5, 5, (byte)0, (byte)0, Blocks.copperWall.id);
world.addDarkness(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
byte darkness = tiles[x][y].rotation();
int distance = Math.abs(x - 7) + Math.abs(y - 7);
assertEquals(Math.min(Math.max(distance - 1, 0), 4), darkness);
}
}
}
}