Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66ff7b1592 | ||
|
|
be32810608 | ||
|
|
88f48599a0 | ||
|
|
014e2ac8b8 | ||
|
|
bae88ec416 | ||
|
|
46230ad307 | ||
|
|
963a207194 | ||
|
|
59419e13d9 | ||
|
|
b370e04035 | ||
|
|
dfd6e7a07b | ||
|
|
a873560d4e | ||
|
|
c6777bd219 | ||
|
|
8eb4d25638 | ||
|
|
191c6e9016 | ||
|
|
52af0b1db9 | ||
|
|
492cb49805 | ||
|
|
b2e70a44be | ||
|
|
452a8aaa0c | ||
|
|
c3f392a01c | ||
|
|
7d9b6a41fc | ||
|
|
f4f4cf3947 | ||
|
|
28e930eb4b | ||
|
|
301387fb32 | ||
|
|
5955387bdd | ||
|
|
150491ff2c | ||
|
|
ddef78c7d8 | ||
|
|
dc435d88f4 |
@@ -1,8 +1,5 @@
|
|||||||
## Translating for Mindustry
|
## Translating for Mindustry
|
||||||
|
|
||||||
**DISCLAIMER:** *Currently, 4.0 is far from done, which means that things such as block names, descriptions, and core text will be changing often. If you begin translating now, you might have to re-do large chunks of the bundle before final release.*
|
|
||||||
|
|
||||||
|
|
||||||
To begin, log in to your GitHub account, or if you don't have one yet, create it [here](https://github.com/).
|
To begin, log in to your GitHub account, or if you don't have one yet, create it [here](https://github.com/).
|
||||||
|
|
||||||
Consult [this list](https://www.science.co.il/language/Locale-codes.php) to find the locale code for your language. Once you've found it,
|
Consult [this list](https://www.science.co.il/language/Locale-codes.php) to find the locale code for your language. Once you've found it,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
load.addStatement(name + " = io.anuke.arc.Core.audio."+loadMethod+"(io.anuke.arc.Core.files.internal(io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? $S : $S))",
|
load.addStatement(name + " = io.anuke.arc.Core.audio."+loadMethod+"(io.anuke.arc.Core.files.internal(io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? $S : $S))",
|
||||||
path.substring(path.lastIndexOf("/") + 1) + "/" + fname, (path.substring(path.lastIndexOf("/") + 1) + "/" + fname).replace(".ogg", ".caf"));
|
path.substring(path.lastIndexOf("/") + 1) + "/" + fname, (path.substring(path.lastIndexOf("/") + 1) + "/" + fname).replace(".ogg", ".mp3"));
|
||||||
dispose.addStatement(name + ".dispose()");
|
dispose.addStatement(name + ".dispose()");
|
||||||
dispose.addStatement(name + " = null");
|
dispose.addStatement(name + " = null");
|
||||||
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new io.anuke.arc.audio.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new io.anuke.arc.audio.mock.Mock" + rtype.substring(rtype.lastIndexOf(".") + 1)+ "()").build());
|
||||||
|
|||||||
@@ -312,6 +312,5 @@ project(":net"){
|
|||||||
dependencies{
|
dependencies{
|
||||||
compile project(":core")
|
compile project(":core")
|
||||||
compile "org.lz4:lz4-java:1.4.1"
|
compile "org.lz4:lz4-java:1.4.1"
|
||||||
compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ server.kicked.nameEmpty = Your chosen name is invalid.
|
|||||||
server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted.
|
server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted.
|
||||||
server.kicked.customClient = This server does not support custom builds. Download an official version.
|
server.kicked.customClient = This server does not support custom builds. Download an official version.
|
||||||
server.kicked.gameover = Game over!
|
server.kicked.gameover = Game over!
|
||||||
|
server.versions = Your version:[accent] {0}[]\nServer version:[accent] {1}[]
|
||||||
host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [lightgray]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[lightgray]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. Note that public networks sometimes do not allow server discovery.
|
host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [lightgray]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[lightgray]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. Note that public networks sometimes do not allow server discovery.
|
||||||
join.info = Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[lightgray]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP.
|
join.info = Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[lightgray]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP.
|
||||||
hostserver = Host Multiplayer Game
|
hostserver = Host Multiplayer Game
|
||||||
@@ -124,7 +125,7 @@ save.new = New Save
|
|||||||
save.overwrite = Are you sure you want to overwrite\nthis save slot?
|
save.overwrite = Are you sure you want to overwrite\nthis save slot?
|
||||||
overwrite = Overwrite
|
overwrite = Overwrite
|
||||||
save.none = No saves found!
|
save.none = No saves found!
|
||||||
saveload = [accent]Saving...
|
saveload = Saving...
|
||||||
savefail = Failed to save game!
|
savefail = Failed to save game!
|
||||||
save.delete.confirm = Are you sure you want to delete this save?
|
save.delete.confirm = Are you sure you want to delete this save?
|
||||||
save.delete = Delete
|
save.delete = Delete
|
||||||
@@ -371,7 +372,7 @@ zone.crags.name = Crags
|
|||||||
zone.fungalPass.name = Fungal Pass
|
zone.fungalPass.name = Fungal Pass
|
||||||
|
|
||||||
zone.groundZero.description = The optimal location to begin once more. Low enemy threat. Few resources.\nGather as much lead and copper as possible.\nMove on.
|
zone.groundZero.description = The optimal location to begin once more. Low enemy threat. Few resources.\nGather as much lead and copper as possible.\nMove on.
|
||||||
zone.frozenForest.description = Even here, closer to mountains, the spores have spread. The fridgid temperatures cannot contain them forever.\n\nBegin the venture into power. Build combustion generators. Learn to use menders.
|
zone.frozenForest.description = Even here, closer to mountains, the spores have spread. The frigid temperatures cannot contain them forever.\n\nBegin the venture into power. Build combustion generators. Learn to use menders.
|
||||||
zone.desertWastes.description = These wastes are vast, unpredictable, and criss-crossed with derelict sector structures.\nCoal is present in the region. Burn it for power, or synthesize graphite.\n\n[lightgray]This landing location cannot be guaranteed.
|
zone.desertWastes.description = These wastes are vast, unpredictable, and criss-crossed with derelict sector structures.\nCoal is present in the region. Burn it for power, or synthesize graphite.\n\n[lightgray]This landing location cannot be guaranteed.
|
||||||
zone.saltFlats.description = On the outskirts of the desert lie the Salt Flats. Few resources can be found in this location.\n\nThe enemy has erected a resource storage complex here. Eradicate their core. Leave nothing standing.
|
zone.saltFlats.description = On the outskirts of the desert lie the Salt Flats. Few resources can be found in this location.\n\nThe enemy has erected a resource storage complex here. Eradicate their core. Leave nothing standing.
|
||||||
zone.craters.description = Water has accumulated in this crater, relic of the old wars. Reclaim the area. Collect sand. Smelt metaglass. Pump water to cool turrets and drills.
|
zone.craters.description = Water has accumulated in this crater, relic of the old wars. Reclaim the area. Collect sand. Smelt metaglass. Pump water to cool turrets and drills.
|
||||||
|
|||||||
BIN
core/assets/saves/settings.bin
Normal file
BIN
core/assets/saves/settings_backup.bin
Normal file
@@ -188,7 +188,8 @@
|
|||||||
up: button,
|
up: button,
|
||||||
over: button-over,
|
over: button-over,
|
||||||
imageDisabledColor: gray,
|
imageDisabledColor: gray,
|
||||||
imageUpColor: white
|
imageUpColor: white,
|
||||||
|
disabled: button-disabled
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
up: button-over,
|
up: button-over,
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class BlockIndexer{
|
|||||||
|
|
||||||
ObjectSet<Tile> set = damagedTiles[team.ordinal()];
|
ObjectSet<Tile> set = damagedTiles[team.ordinal()];
|
||||||
for(Tile tile : set){
|
for(Tile tile : set){
|
||||||
if((tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()) && !(tile.block() instanceof BuildBlock)){
|
if((tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()) || tile.block() instanceof BuildBlock){
|
||||||
returnArray.add(tile);
|
returnArray.add(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class Control implements ApplicationListener{
|
|||||||
});
|
});
|
||||||
|
|
||||||
Events.on(PlayEvent.class, event -> {
|
Events.on(PlayEvent.class, event -> {
|
||||||
player.setTeam(defaultTeam);
|
player.setTeam(state.rules.pvp ? netServer.assignTeam(player, playerGroup.all()) : defaultTeam);
|
||||||
player.setDead(true);
|
player.setDead(true);
|
||||||
player.add();
|
player.add();
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ public class Control implements ApplicationListener{
|
|||||||
public void playMap(Map map, Rules rules){
|
public void playMap(Map map, Rules rules){
|
||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
logic.reset();
|
logic.reset();
|
||||||
world.loadMap(map);
|
world.loadMap(map, rules);
|
||||||
state.rules = rules;
|
state.rules = rules;
|
||||||
logic.play();
|
logic.play();
|
||||||
});
|
});
|
||||||
@@ -350,35 +350,37 @@ public class Control implements ApplicationListener{
|
|||||||
|
|
||||||
//display UI scale changed dialog
|
//display UI scale changed dialog
|
||||||
if(Core.settings.getBool("uiscalechanged", false)){
|
if(Core.settings.getBool("uiscalechanged", false)){
|
||||||
FloatingDialog dialog = new FloatingDialog("$confirm");
|
Core.app.post(() -> Core.app.post(() -> {
|
||||||
|
FloatingDialog dialog = new FloatingDialog("$confirm");
|
||||||
|
dialog.setFillParent(true);
|
||||||
|
|
||||||
float[] countdown = {60 * 11};
|
float[] countdown = {60 * 11};
|
||||||
Runnable exit = () -> {
|
Runnable exit = () -> {
|
||||||
Core.settings.put("uiscale", 100);
|
Core.settings.put("uiscale", 100);
|
||||||
Core.settings.put("uiscalechanged", false);
|
Core.settings.put("uiscalechanged", false);
|
||||||
settings.save();
|
settings.save();
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
Core.app.exit();
|
Core.app.exit();
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.setFillParent(false);
|
dialog.cont.label(() -> {
|
||||||
dialog.cont.label(() -> {
|
if(countdown[0] <= 0){
|
||||||
if(countdown[0] <= 0){
|
exit.run();
|
||||||
exit.run();
|
}
|
||||||
}
|
return Core.bundle.format("uiscale.reset", (int)((countdown[0] -= Time.delta()) / 60f));
|
||||||
return Core.bundle.format("uiscale.reset", (int)((countdown[0] -= Time.delta()) / 60f));
|
}).pad(10f).expand().center();
|
||||||
}).pad(10f).expand().left();
|
|
||||||
|
|
||||||
dialog.buttons.defaults().size(200f, 60f);
|
dialog.buttons.defaults().size(200f, 60f);
|
||||||
dialog.buttons.addButton("$uiscale.cancel", exit);
|
dialog.buttons.addButton("$uiscale.cancel", exit);
|
||||||
|
|
||||||
dialog.buttons.addButton("$ok", () -> {
|
dialog.buttons.addButton("$ok", () -> {
|
||||||
Core.settings.put("uiscalechanged", false);
|
Core.settings.put("uiscalechanged", false);
|
||||||
settings.save();
|
settings.save();
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
Core.app.post(dialog::show);
|
dialog.show();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import static io.anuke.mindustry.Vars.*;
|
|||||||
|
|
||||||
public class NetServer implements ApplicationListener{
|
public class NetServer implements ApplicationListener{
|
||||||
public final static int maxSnapshotSize = 430;
|
public final static int maxSnapshotSize = 430;
|
||||||
private final static float serverSyncTime = 15, kickDuration = 30 * 1000;
|
private final static float serverSyncTime = 12, kickDuration = 30 * 1000;
|
||||||
private final static Vector2 vector = new Vector2();
|
private final static Vector2 vector = new Vector2();
|
||||||
private final static Rectangle viewport = new Rectangle();
|
private final static Rectangle viewport = new Rectangle();
|
||||||
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
|
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
|
||||||
@@ -176,7 +176,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
|
|
||||||
//playing in pvp mode automatically assigns players to teams
|
//playing in pvp mode automatically assigns players to teams
|
||||||
if(state.rules.pvp){
|
if(state.rules.pvp){
|
||||||
player.setTeam(assignTeam(playerGroup.all()));
|
player.setTeam(assignTeam(player, playerGroup.all()));
|
||||||
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
|
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,13 +194,13 @@ public class NetServer implements ApplicationListener{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Team assignTeam(Iterable<Player> players){
|
public Team assignTeam(Player current, Iterable<Player> players){
|
||||||
//find team with minimum amount of players and auto-assign player to that.
|
//find team with minimum amount of players and auto-assign player to that.
|
||||||
return Structs.findMin(Team.all, team -> {
|
return Structs.findMin(Team.all, team -> {
|
||||||
if(state.teams.isActive(team) && !state.teams.get(team).cores.isEmpty()){
|
if(state.teams.isActive(team) && !state.teams.get(team).cores.isEmpty()){
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for(Player other : players){
|
for(Player other : players){
|
||||||
if(other.getTeam() == team){
|
if(other.getTeam() == team && other != current){
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,6 +220,10 @@ public class World implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadMap(Map map){
|
public void loadMap(Map map){
|
||||||
|
loadMap(map, new Rules());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMap(Map map, Rules checkRules){
|
||||||
try{
|
try{
|
||||||
SaveIO.load(map.file, new FilterContext(map));
|
SaveIO.load(map.file, new FilterContext(map));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
@@ -238,20 +242,21 @@ public class World implements ApplicationListener{
|
|||||||
invalidMap = false;
|
invalidMap = false;
|
||||||
|
|
||||||
if(!headless){
|
if(!headless){
|
||||||
if(state.teams.get(defaultTeam).cores.size == 0){
|
if(state.teams.get(defaultTeam).cores.size == 0 && !checkRules.pvp){
|
||||||
ui.showError("$map.nospawn");
|
ui.showError("$map.nospawn");
|
||||||
invalidMap = true;
|
invalidMap = true;
|
||||||
}else if(state.rules.pvp){ //pvp maps need two cores to be valid
|
}else if(checkRules.pvp){ //pvp maps need two cores to be valid
|
||||||
invalidMap = true;
|
int teams = 0;
|
||||||
for(Team team : Team.all){
|
for(Team team : Team.all){
|
||||||
if(state.teams.get(team).cores.size != 0 && team != defaultTeam){
|
if(state.teams.get(team).cores.size != 0){
|
||||||
invalidMap = false;
|
teams ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(invalidMap){
|
if(teams < 2){
|
||||||
|
invalidMap = true;
|
||||||
ui.showError("$map.nospawn.pvp");
|
ui.showError("$map.nospawn.pvp");
|
||||||
}
|
}
|
||||||
}else if(state.rules.attackMode){ //pvp maps need two cores to be valid
|
}else if(checkRules.attackMode){ //attack maps need two cores to be valid
|
||||||
invalidMap = state.teams.get(waveTeam).cores.isEmpty();
|
invalidMap = state.teams.get(waveTeam).cores.isEmpty();
|
||||||
if(invalidMap){
|
if(invalidMap){
|
||||||
ui.showError("$map.nospawn.attack");
|
ui.showError("$map.nospawn.attack");
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class Predict{
|
|||||||
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
||||||
*/
|
*/
|
||||||
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){
|
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){
|
||||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX(), dst.getTargetVelocityY() - src.getTargetVelocityY(), v);
|
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/2f, dst.getTargetVelocityY() - src.getTargetVelocityY()/2f, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector2 quad(float a, float b, float c){
|
private static Vector2 quad(float a, float b, float c){
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
package io.anuke.mindustry.entities.traits;
|
package io.anuke.mindustry.entities.traits;
|
||||||
|
|
||||||
import io.anuke.annotations.Annotations.*;
|
import io.anuke.annotations.Annotations.*;
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.arc.Events;
|
|
||||||
import io.anuke.arc.collection.Array;
|
|
||||||
import io.anuke.arc.collection.Queue;
|
import io.anuke.arc.collection.Queue;
|
||||||
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.graphics.g2d.*;
|
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.content.*;
|
||||||
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.entities.type.Unit;
|
import io.anuke.mindustry.gen.*;
|
||||||
import io.anuke.mindustry.game.EventType.BuildSelectEvent;
|
import io.anuke.mindustry.graphics.*;
|
||||||
import io.anuke.mindustry.gen.Call;
|
|
||||||
import io.anuke.mindustry.graphics.Pal;
|
|
||||||
import io.anuke.mindustry.net.Net;
|
|
||||||
import io.anuke.mindustry.world.*;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.blocks.BuildBlock;
|
import io.anuke.mindustry.world.blocks.*;
|
||||||
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
|
import io.anuke.mindustry.world.blocks.BuildBlock.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.removal;
|
import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.*;
|
||||||
import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.tmptr;
|
|
||||||
|
|
||||||
/** Interface for units that build things.*/
|
/** Interface for units that build things.*/
|
||||||
public interface BuilderTrait extends Entity, TeamTrait{
|
public interface BuilderTrait extends Entity, TeamTrait{
|
||||||
@@ -106,19 +101,14 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
|
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//progress is synced, thus not updated clientside
|
//deconstructing is 2x as fast
|
||||||
if(!Net.client()){
|
if(current.breaking){
|
||||||
//deconstructing is 2x as fast
|
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||||
if(current.breaking){
|
|
||||||
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
|
||||||
}else{
|
|
||||||
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
current.progress = entity.progress();
|
|
||||||
}else{
|
}else{
|
||||||
entity.progress = current.progress;
|
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current.progress = entity.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the queue for storing build requests. */
|
/** Returns the queue for storing build requests. */
|
||||||
@@ -135,7 +125,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
default void writeBuilding(DataOutput output) throws IOException{
|
default void writeBuilding(DataOutput output) throws IOException{
|
||||||
BuildRequest request = buildRequest();
|
BuildRequest request = buildRequest();
|
||||||
|
|
||||||
if(request != null){
|
if(request != null && (request.block != null || request.breaking)){
|
||||||
output.writeByte(request.breaking ? 1 : 0);
|
output.writeByte(request.breaking ? 1 : 0);
|
||||||
output.writeInt(Pos.get(request.x, request.y));
|
output.writeInt(Pos.get(request.x, request.y));
|
||||||
output.writeFloat(request.progress);
|
output.writeFloat(request.progress);
|
||||||
@@ -174,7 +164,11 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
if(applyChanges){
|
if(applyChanges){
|
||||||
buildQueue().addLast(request);
|
buildQueue().addLast(request);
|
||||||
}else if(isBuilding()){
|
}else if(isBuilding()){
|
||||||
buildRequest().progress = progress;
|
BuildRequest last = buildRequest();
|
||||||
|
last.progress = progress;
|
||||||
|
if(last.tile() != null && last.tile().entity instanceof BuildEntity){
|
||||||
|
((BuildEntity)last.tile().entity).progress = progress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,6 +279,10 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
this.breaking = true;
|
this.breaking = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Tile tile(){
|
||||||
|
return world.tile(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "BuildRequest{" +
|
return "BuildRequest{" +
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||||||
private Tile mining;
|
private Tile mining;
|
||||||
private Vector2 movement = new Vector2();
|
private Vector2 movement = new Vector2();
|
||||||
private boolean moved;
|
private boolean moved;
|
||||||
private SoundLoop buildSound = new SoundLoop(Sounds.build, 0.75f);
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -131,11 +130,6 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removed(){
|
|
||||||
buildSound.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float drag(){
|
public float drag(){
|
||||||
return mech.drag;
|
return mech.drag;
|
||||||
@@ -518,7 +512,9 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||||||
}
|
}
|
||||||
|
|
||||||
BuildRequest request = buildRequest();
|
BuildRequest request = buildRequest();
|
||||||
buildSound.update(request == null ? x : request.x * tilesize, request == null ? y : request.y * tilesize, isBuilding() && (Mathf.within(request.x * tilesize, request.y * tilesize, x, y, placeDistance) || state.isEditor()));
|
if(isBuilding() && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){
|
||||||
|
loops.play(Sounds.build, request.tile(), 0.75f);
|
||||||
|
}
|
||||||
|
|
||||||
if(isDead()){
|
if(isDead()){
|
||||||
isBoosting = false;
|
isBoosting = false;
|
||||||
@@ -531,7 +527,9 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||||||
spawner = null;
|
spawner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
avoidOthers();
|
if(isLocal || Net.server()){
|
||||||
|
avoidOthers();
|
||||||
|
}
|
||||||
|
|
||||||
Tile tile = world.tileWorld(x, y);
|
Tile tile = world.tileWorld(x, y);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import io.anuke.mindustry.entities.type.TileEntity;
|
|||||||
import io.anuke.mindustry.entities.units.UnitState;
|
import io.anuke.mindustry.entities.units.UnitState;
|
||||||
import io.anuke.mindustry.world.Pos;
|
import io.anuke.mindustry.world.Pos;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.blocks.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -23,6 +24,10 @@ public class RepairDrone extends BaseDrone{
|
|||||||
target = Units.findDamagedTile(team, x, y);
|
target = Units.findDamagedTile(team, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(target instanceof TileEntity && ((TileEntity)target).block instanceof BuildBlock){
|
||||||
|
target = null;
|
||||||
|
}
|
||||||
|
|
||||||
if(target != null){
|
if(target != null){
|
||||||
if(target.dst(RepairDrone.this) > type.range){
|
if(target.dst(RepairDrone.this) > type.range){
|
||||||
circle(type.range * 0.9f);
|
circle(type.range * 0.9f);
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ public class Stats{
|
|||||||
public RankResult calculateRank(Zone zone, boolean launched){
|
public RankResult calculateRank(Zone zone, boolean launched){
|
||||||
float score = 0;
|
float score = 0;
|
||||||
|
|
||||||
//each new launch period adds onto the rank 'points'
|
if(launched && zone.getRules().attackMode){
|
||||||
if(wavesLasted >= zone.conditionWave){
|
score += 3f;
|
||||||
|
}else if(wavesLasted >= zone.conditionWave){
|
||||||
|
//each new launch period adds onto the rank 'points'
|
||||||
score += (float)((wavesLasted - zone.conditionWave) / zone.launchPeriod + 1) * 1.2f;
|
score += (float)((wavesLasted - zone.conditionWave) / zone.launchPeriod + 1) * 1.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
player.isShooting = false;
|
player.isShooting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && !ui.chatfrag.chatOpen() && !(scene.getKeyboardFocus() instanceof TextField)){
|
if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && (scene.getKeyboardFocus() == ui.minimap || !scene.hasDialog()) && !ui.chatfrag.chatOpen() && !(scene.getKeyboardFocus() instanceof TextField)){
|
||||||
if(!ui.minimap.isShown()){
|
if(!ui.minimap.isShown()){
|
||||||
ui.minimap.show();
|
ui.minimap.show();
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -214,7 +214,12 @@ public class Maps implements Disposable{
|
|||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
}else{
|
}else{
|
||||||
return JsonIO.read(Array.class, str);
|
try{
|
||||||
|
return JsonIO.read(Array.class, str);
|
||||||
|
}catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
return readFilters("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package io.anuke.mindustry.net;
|
package io.anuke.mindustry.net;
|
||||||
|
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.*;
|
||||||
import io.anuke.arc.math.geom.Vector2;
|
import io.anuke.arc.math.geom.*;
|
||||||
import io.anuke.arc.util.Time;
|
import io.anuke.arc.util.*;
|
||||||
|
|
||||||
public class Interpolator{
|
public class Interpolator{
|
||||||
//used for movement
|
//used for movement
|
||||||
public Vector2 target = new Vector2();
|
public Vector2 target = new Vector2();
|
||||||
public Vector2 last = new Vector2();
|
public Vector2 last = new Vector2();
|
||||||
public float[] targets = {};
|
public float[] targets = {};
|
||||||
|
public float[] lasts = {};
|
||||||
public long lastUpdated, updateSpacing;
|
public long lastUpdated, updateSpacing;
|
||||||
|
|
||||||
//current state
|
//current state
|
||||||
@@ -21,6 +22,12 @@ public class Interpolator{
|
|||||||
lastUpdated = Time.millis();
|
lastUpdated = Time.millis();
|
||||||
|
|
||||||
targets = target1ds;
|
targets = target1ds;
|
||||||
|
if(lasts.length != values.length){
|
||||||
|
lasts = new float[values.length];
|
||||||
|
}
|
||||||
|
for(int i = 0; i < values.length; i++){
|
||||||
|
lasts[i] = values[i];
|
||||||
|
}
|
||||||
last.set(cx, cy);
|
last.set(cx, cy);
|
||||||
target.set(x, y);
|
target.set(x, y);
|
||||||
}
|
}
|
||||||
@@ -46,8 +53,12 @@ public class Interpolator{
|
|||||||
values = new float[targets.length];
|
values = new float[targets.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lasts.length != targets.length){
|
||||||
|
lasts = new float[targets.length];
|
||||||
|
}
|
||||||
|
|
||||||
for(int i = 0; i < values.length; i++){
|
for(int i = 0; i < values.length; i++){
|
||||||
values[i] = Mathf.slerp(values[i], targets[i], alpha);
|
values[i] = Mathf.slerp(lasts[i], targets[i], alpha);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
pos.set(target);
|
pos.set(target);
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ public class Net{
|
|||||||
/**
|
/**
|
||||||
* Returns a list of all connections IDs.
|
* Returns a list of all connections IDs.
|
||||||
*/
|
*/
|
||||||
public static Array<NetConnection> getConnections(){
|
public static Iterable<NetConnection> getConnections(){
|
||||||
return (Array<NetConnection>)serverProvider.getConnections();
|
return (Iterable<NetConnection>)serverProvider.getConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,7 +326,7 @@ public class Net{
|
|||||||
|
|
||||||
public static void dispose(){
|
public static void dispose(){
|
||||||
if(clientProvider != null) clientProvider.dispose();
|
if(clientProvider != null) clientProvider.dispose();
|
||||||
if(serverProvider != null) serverProvider.dispose();
|
if(serverProvider != null) serverProvider.close();
|
||||||
clientProvider = null;
|
clientProvider = null;
|
||||||
serverProvider = null;
|
serverProvider = null;
|
||||||
server = false;
|
server = false;
|
||||||
@@ -377,16 +377,53 @@ public class Net{
|
|||||||
void host(int port) throws IOException;
|
void host(int port) throws IOException;
|
||||||
|
|
||||||
/** Sends a large stream of data to a specific client. */
|
/** Sends a large stream of data to a specific client. */
|
||||||
void sendStream(int id, Streamable stream);
|
default void sendStream(int id, Streamable stream){
|
||||||
|
NetConnection connection = getByID(id);
|
||||||
|
if(connection == null) return;
|
||||||
|
try{
|
||||||
|
int cid;
|
||||||
|
StreamBegin begin = new StreamBegin();
|
||||||
|
begin.total = stream.stream.available();
|
||||||
|
begin.type = Registrator.getID(stream.getClass());
|
||||||
|
connection.send(begin, SendMode.tcp);
|
||||||
|
cid = begin.id;
|
||||||
|
|
||||||
/** Send an object to everyone connected. */
|
while(stream.stream.available() > 0){
|
||||||
void send(Object object, SendMode mode);
|
byte[] bytes = new byte[Math.min(512, stream.stream.available())];
|
||||||
|
stream.stream.read(bytes);
|
||||||
|
|
||||||
/** Send an object to a specific client ID. */
|
StreamChunk chunk = new StreamChunk();
|
||||||
void sendTo(int id, Object object, SendMode mode);
|
chunk.id = cid;
|
||||||
|
chunk.data = bytes;
|
||||||
|
connection.send(chunk, SendMode.tcp);
|
||||||
|
}
|
||||||
|
}catch(IOException e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Send an object to everyone <i>except</i> a client ID. */
|
default void send(Object object, SendMode mode){
|
||||||
void sendExcept(int id, Object object, SendMode mode);
|
for(NetConnection con : getConnections()){
|
||||||
|
con.send(object, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendTo(int id, Object object, SendMode mode){
|
||||||
|
NetConnection conn = getByID(id);
|
||||||
|
if(conn == null){
|
||||||
|
Log.err("Failed to find connection with ID {0}.", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn.send(object, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendExcept(int id, Object object, SendMode mode){
|
||||||
|
for(NetConnection con : getConnections()){
|
||||||
|
if(con.id != id){
|
||||||
|
con.send(object, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Close the server connection. */
|
/** Close the server connection. */
|
||||||
void close();
|
void close();
|
||||||
@@ -395,12 +432,9 @@ public class Net{
|
|||||||
byte[] compressSnapshot(byte[] input);
|
byte[] compressSnapshot(byte[] input);
|
||||||
|
|
||||||
/** Return all connected users. */
|
/** Return all connected users. */
|
||||||
Array<? extends NetConnection> getConnections();
|
Iterable<? extends NetConnection> getConnections();
|
||||||
|
|
||||||
/** Returns a connection by ID. */
|
/** Returns a connection by ID. */
|
||||||
NetConnection getByID(int id);
|
NetConnection getByID(int id);
|
||||||
|
|
||||||
/** Close all connections. */
|
|
||||||
void dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package io.anuke.mindustry.net;
|
|||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
|
|
||||||
public abstract class NetConnection{
|
public abstract class NetConnection{
|
||||||
|
private static int lastID;
|
||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
public final String address;
|
public final String address;
|
||||||
|
|
||||||
@@ -18,8 +20,9 @@ public abstract class NetConnection{
|
|||||||
public boolean hasBegunConnecting = false;
|
public boolean hasBegunConnecting = false;
|
||||||
public float viewWidth, viewHeight, viewX, viewY;
|
public float viewWidth, viewHeight, viewX, viewY;
|
||||||
|
|
||||||
public NetConnection(int id, String address){
|
/** Assigns this connection a unique ID. No two connections will ever have the same ID.*/
|
||||||
this.id = id;
|
public NetConnection(String address){
|
||||||
|
this.id = lastID++;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ public class FileChooser extends FloatingDialog{
|
|||||||
private Predicate<FileHandle> filter;
|
private Predicate<FileHandle> filter;
|
||||||
private Consumer<FileHandle> selectListener;
|
private Consumer<FileHandle> selectListener;
|
||||||
private boolean open;
|
private boolean open;
|
||||||
private int lastWidth = Core.graphics.getWidth(), lastHeight = Core.graphics.getHeight();
|
|
||||||
|
|
||||||
public static final Predicate<String> pngFiles = str -> str.equals("png");
|
public static final Predicate<String> pngFiles = str -> str.equals("png");
|
||||||
public static final Predicate<String> anyMapFiles = str -> str.equals(oldMapExtension) || str.equals(mapExtension);
|
public static final Predicate<String> anyMapFiles = str -> str.equals(oldMapExtension) || str.equals(mapExtension);
|
||||||
@@ -44,12 +43,14 @@ public class FileChooser extends FloatingDialog{
|
|||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.selectListener = result;
|
this.selectListener = result;
|
||||||
|
|
||||||
update(() -> {
|
onResize(() -> {
|
||||||
if(Core.graphics.getWidth() != lastWidth || Core.graphics.getHeight() != lastHeight){
|
cont.clear();
|
||||||
updateFiles(false);
|
setupWidgets();
|
||||||
lastHeight = Core.graphics.getHeight();
|
});
|
||||||
lastWidth = Core.graphics.getWidth();
|
|
||||||
}
|
shown(() -> {
|
||||||
|
cont.clear();
|
||||||
|
setupWidgets();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +122,9 @@ public class FileChooser extends FloatingDialog{
|
|||||||
forward.resizeImage(isize);
|
forward.resizeImage(isize);
|
||||||
|
|
||||||
forward.clicked(() -> stack.forward());
|
forward.clicked(() -> stack.forward());
|
||||||
|
|
||||||
back.clicked(() -> stack.back());
|
back.clicked(() -> stack.back());
|
||||||
|
forward.setDisabled(() -> !stack.canForward());
|
||||||
|
back.setDisabled(() -> !stack.canBack());
|
||||||
|
|
||||||
ImageButton home = new ImageButton("icon-home");
|
ImageButton home = new ImageButton("icon-home");
|
||||||
home.resizeImage(isize);
|
home.resizeImage(isize);
|
||||||
@@ -206,7 +208,7 @@ public class FileChooser extends FloatingDialog{
|
|||||||
|
|
||||||
//macs are confined to the Downloads/ directory
|
//macs are confined to the Downloads/ directory
|
||||||
if(!OS.isMac){
|
if(!OS.isMac){
|
||||||
Image upimage = new Image("icon-folder-parent");
|
Image upimage = new Image("icon-folder-parent-small");
|
||||||
TextButton upbutton = new TextButton(".." + directory.toString(), "clear-toggle");
|
TextButton upbutton = new TextButton(".." + directory.toString(), "clear-toggle");
|
||||||
upbutton.clicked(() -> {
|
upbutton.clicked(() -> {
|
||||||
directory = directory.parent();
|
directory = directory.parent();
|
||||||
@@ -214,7 +216,7 @@ public class FileChooser extends FloatingDialog{
|
|||||||
updateFiles(true);
|
updateFiles(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
upbutton.left().add(upimage).padRight(4f).size(iconsize);
|
upbutton.left().add(upimage).padRight(4f).size(iconsizesmall).padLeft(4);
|
||||||
upbutton.getLabel().setAlignment(Align.left);
|
upbutton.getLabel().setAlignment(Align.left);
|
||||||
upbutton.getCells().reverse();
|
upbutton.getCells().reverse();
|
||||||
|
|
||||||
@@ -248,9 +250,9 @@ public class FileChooser extends FloatingDialog{
|
|||||||
button.setChecked(filename.equals(filefield.getText()));
|
button.setChecked(filename.equals(filefield.getText()));
|
||||||
});
|
});
|
||||||
|
|
||||||
Image image = new Image(file.isDirectory() ? "icon-folder" : "icon-file-text");
|
Image image = new Image(file.isDirectory() ? "icon-folder-small" : "icon-file-text-small");
|
||||||
|
|
||||||
button.add(image).padRight(4f).size(iconsize);
|
button.add(image).padRight(4f).padLeft(4).size(iconsizesmall);
|
||||||
button.getCells().reverse();
|
button.getCells().reverse();
|
||||||
files.top().left().add(button).align(Align.topLeft).fillX().expandX()
|
files.top().left().add(button).align(Align.topLeft).fillX().expandX()
|
||||||
.height(50).pad(2).padTop(0).padBottom(0).colspan(2);
|
.height(50).pad(2).padTop(0).padBottom(0).colspan(2);
|
||||||
@@ -273,17 +275,6 @@ public class FileChooser extends FloatingDialog{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog show(){
|
|
||||||
Time.runTask(2f, () -> {
|
|
||||||
cont.clear();
|
|
||||||
setupWidgets();
|
|
||||||
super.show();
|
|
||||||
Core.scene.setScrollFocus(pane);
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FileHistory{
|
public class FileHistory{
|
||||||
private Array<FileHandle> history = new Array<>();
|
private Array<FileHandle> history = new Array<>();
|
||||||
private int index;
|
private int index;
|
||||||
|
|||||||
@@ -39,18 +39,6 @@ public class FloatingDialog extends Dialog{
|
|||||||
state.set(State.paused);
|
state.set(State.paused);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
boolean[] done = {false};
|
|
||||||
|
|
||||||
shown(() -> Core.app.post(() ->
|
|
||||||
forEach(child -> {
|
|
||||||
if(done[0]) return;
|
|
||||||
|
|
||||||
if(child instanceof ScrollPane){
|
|
||||||
Core.scene.setScrollFocus(child);
|
|
||||||
done[0] = true;
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FloatingDialog(String title){
|
public FloatingDialog(String title){
|
||||||
@@ -59,8 +47,9 @@ public class FloatingDialog extends Dialog{
|
|||||||
|
|
||||||
protected void onResize(Runnable run){
|
protected void onResize(Runnable run){
|
||||||
Events.on(ResizeEvent.class, event -> {
|
Events.on(ResizeEvent.class, event -> {
|
||||||
if(isShown()){
|
if(isShown() && Core.scene.getDialog() == this){
|
||||||
run.run();
|
run.run();
|
||||||
|
updateScrollFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package io.anuke.mindustry.ui.dialogs;
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.mindustry.core.GameState.State;
|
import io.anuke.mindustry.core.GameState.*;
|
||||||
import io.anuke.mindustry.game.Stats.RankResult;
|
import io.anuke.mindustry.game.Stats.*;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.*;
|
||||||
import io.anuke.mindustry.type.Item;
|
import io.anuke.mindustry.type.*;
|
||||||
import io.anuke.mindustry.type.Item.Icon;
|
import io.anuke.mindustry.type.Item.*;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
package io.anuke.mindustry.ui.dialogs;
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
import io.anuke.annotations.Annotations.Serialize;
|
import io.anuke.annotations.Annotations.*;
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.graphics.Color;
|
import io.anuke.arc.graphics.*;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.*;
|
||||||
import io.anuke.arc.scene.style.Drawable;
|
import io.anuke.arc.scene.style.*;
|
||||||
import io.anuke.arc.scene.ui.*;
|
import io.anuke.arc.scene.ui.*;
|
||||||
import io.anuke.arc.scene.ui.layout.Cell;
|
import io.anuke.arc.scene.ui.layout.*;
|
||||||
import io.anuke.arc.scene.ui.layout.Table;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.arc.util.Strings;
|
import io.anuke.mindustry.*;
|
||||||
import io.anuke.arc.util.Time;
|
import io.anuke.mindustry.core.*;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.game.*;
|
||||||
import io.anuke.mindustry.core.Platform;
|
|
||||||
import io.anuke.mindustry.game.Version;
|
|
||||||
import io.anuke.mindustry.net.Host;
|
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.mindustry.net.*;
|
||||||
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -97,9 +96,14 @@ public class JoinDialog extends FloatingDialog{
|
|||||||
|
|
||||||
TextButton button = buttons[0] = remote.addButton("[accent]" + server.displayIP(), "clear", () -> {
|
TextButton button = buttons[0] = remote.addButton("[accent]" + server.displayIP(), "clear", () -> {
|
||||||
if(!buttons[0].childrenPressed()){
|
if(!buttons[0].childrenPressed()){
|
||||||
connect(server.ip, server.port);
|
if(server.lastHost != null && server.lastHost.version != Version.build && Version.build != -1 && server.lastHost.version != -1){
|
||||||
|
ui.showInfo("[scarlet]" + (server.lastHost.version > Version.build ? KickReason.clientOutdated : KickReason.serverOutdated).toString() + "\n[]" +
|
||||||
|
Core.bundle.format("server.versions", Version.build, server.lastHost.version));
|
||||||
|
}else{
|
||||||
|
connect(server.ip, server.port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).width(targetWidth()).height(130f).pad(4f).get();
|
}).width(targetWidth()).pad(4f).get();
|
||||||
|
|
||||||
button.getLabel().setWrap(true);
|
button.getLabel().setWrap(true);
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class PausedDialog extends FloatingDialog{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial){
|
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial || Net.client()){
|
||||||
state.set(State.menu);
|
state.set(State.menu);
|
||||||
logic.reset();
|
logic.reset();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -113,9 +113,16 @@ public class ZoneInfoDialog extends FloatingDialog{
|
|||||||
t.add("$zone.resources").padRight(6);
|
t.add("$zone.resources").padRight(6);
|
||||||
|
|
||||||
if(zone.resources.length > 0){
|
if(zone.resources.length > 0){
|
||||||
for(Item item : zone.resources){
|
t.table(r -> {
|
||||||
t.addImage(item.icon(Item.Icon.medium)).size(8 * 3);
|
t.left();
|
||||||
}
|
int i = 0;
|
||||||
|
for(Item item : zone.resources){
|
||||||
|
r.addImage(item.icon(Item.Icon.medium)).size(8 * 3);
|
||||||
|
if(++i % 4 == 0){
|
||||||
|
r.row();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}else{
|
}else{
|
||||||
t.add("$none");
|
t.add("$none");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,9 +242,9 @@ public class HudFragment extends Fragment{
|
|||||||
IntFormat fps = new IntFormat("fps");
|
IntFormat fps = new IntFormat("fps");
|
||||||
IntFormat ping = new IntFormat("ping");
|
IntFormat ping = new IntFormat("ping");
|
||||||
|
|
||||||
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left();
|
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style("outline");
|
||||||
info.row();
|
info.row();
|
||||||
info.label(() -> ping.get(Net.getPing())).visible(Net::client).left();
|
info.label(() -> ping.get(Net.getPing())).visible(Net::client).left().style("outline");
|
||||||
}).top().left();
|
}).top().left();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -294,9 +294,7 @@ public class Block extends BlockStorage{
|
|||||||
/** Called after the block is placed by this client. */
|
/** Called after the block is placed by this client. */
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void playerPlaced(Tile tile){
|
public void playerPlaced(Tile tile){
|
||||||
if(outputsPower && !consumesPower){
|
|
||||||
PowerNode.lastPlaced = tile.pos();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called after the block is placed by anyone. */
|
/** Called after the block is placed by anyone. */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.world.blocks.power;
|
|||||||
|
|
||||||
import io.anuke.annotations.Annotations.*;
|
import io.anuke.annotations.Annotations.*;
|
||||||
import io.anuke.arc.*;
|
import io.anuke.arc.*;
|
||||||
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.function.*;
|
import io.anuke.arc.function.*;
|
||||||
import io.anuke.arc.graphics.*;
|
import io.anuke.arc.graphics.*;
|
||||||
import io.anuke.arc.graphics.g2d.*;
|
import io.anuke.arc.graphics.g2d.*;
|
||||||
@@ -20,9 +21,7 @@ import io.anuke.mindustry.world.meta.*;
|
|||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
public class PowerNode extends PowerBlock{
|
public class PowerNode extends PowerBlock{
|
||||||
//last distribution block placed
|
protected ObjectSet<PowerGraph> graphs = new ObjectSet<>();
|
||||||
public static int lastPlaced = -1;
|
|
||||||
|
|
||||||
protected Vector2 t1 = new Vector2(), t2 = new Vector2();
|
protected Vector2 t1 = new Vector2(), t2 = new Vector2();
|
||||||
protected TextureRegion laser, laserEnd;
|
protected TextureRegion laser, laserEnd;
|
||||||
|
|
||||||
@@ -99,23 +98,11 @@ public class PowerNode extends PowerBlock{
|
|||||||
() -> Mathf.clamp(entity.power.graph.getPowerProduced() / entity.power.graph.getPowerNeeded())));
|
() -> Mathf.clamp(entity.power.graph.getPowerProduced() / entity.power.graph.getPowerNeeded())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void playerPlaced(Tile tile){
|
|
||||||
Tile before = world.tile(lastPlaced);
|
|
||||||
|
|
||||||
if(linkValid(tile, before) && !before.entity.proximity().contains(tile)){
|
|
||||||
Call.linkPowerNodes(null, tile, before);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastPlaced = tile.pos();
|
|
||||||
super.playerPlaced(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void placed(Tile tile){
|
public void placed(Tile tile){
|
||||||
if(Net.client()) return;
|
if(Net.client()) return;
|
||||||
|
|
||||||
Predicate<Tile> valid = other -> other != null && other != tile && ((!other.block().outputsPower && other.block().consumesPower) || (other.block().outputsPower && !other.block().consumesPower)) && linkValid(tile, other)
|
Predicate<Tile> valid = other -> other != null && other != tile && ((!other.block().outputsPower && other.block().consumesPower) || (other.block().outputsPower && !other.block().consumesPower) || other.block() instanceof PowerNode) && linkValid(tile, other)
|
||||||
&& !other.entity.proximity().contains(tile) && other.entity.power.graph != tile.entity.power.graph;
|
&& !other.entity.proximity().contains(tile) && other.entity.power.graph != tile.entity.power.graph;
|
||||||
|
|
||||||
tempTiles.clear();
|
tempTiles.clear();
|
||||||
@@ -132,6 +119,28 @@ public class PowerNode extends PowerBlock{
|
|||||||
super.placed(tile);
|
super.placed(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getPotentialLinks(Tile tile, Consumer<Tile> others){
|
||||||
|
Predicate<Tile> valid = other -> other != null && other != tile && other.entity != null && other.entity.power != null &&
|
||||||
|
((!other.block().outputsPower && other.block().consumesPower) || (other.block().outputsPower && !other.block().consumesPower) || other.block() instanceof PowerNode) &&
|
||||||
|
overlaps(tile.x * tilesize + offset(), tile.y *tilesize + offset(), other, laserRange * tilesize)
|
||||||
|
&& !other.entity.proximity().contains(tile) && !graphs.contains(other.entity.power.graph);
|
||||||
|
|
||||||
|
tempTiles.clear();
|
||||||
|
graphs.clear();
|
||||||
|
Geometry.circle(tile.x, tile.y, (int)(laserRange + 1), (x, y) -> {
|
||||||
|
Tile other = world.ltile(x, y);
|
||||||
|
if(valid.test(other)){
|
||||||
|
tempTiles.add(other);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tempTiles.sort(Structs.comparingFloat(t -> t.dst2(tile)));
|
||||||
|
tempTiles.each(valid, t -> {
|
||||||
|
graphs.add(t.entity.power.graph);
|
||||||
|
others.accept(t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStats(){
|
public void setStats(){
|
||||||
super.setStats();
|
super.setStats();
|
||||||
@@ -213,6 +222,11 @@ public class PowerNode extends PowerBlock{
|
|||||||
Draw.color(Pal.placing);
|
Draw.color(Pal.placing);
|
||||||
Drawf.circles(x * tilesize + offset(), y * tilesize + offset(), laserRange * tilesize);
|
Drawf.circles(x * tilesize + offset(), y * tilesize + offset(), laserRange * tilesize);
|
||||||
|
|
||||||
|
getPotentialLinks(tile, other -> {
|
||||||
|
Drawf.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f + 2f, Pal.place);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
for(int cx = (int)(x - laserRange - 1); cx <= x + laserRange + 1; cx++){
|
for(int cx = (int)(x - laserRange - 1); cx <= x + laserRange + 1; cx++){
|
||||||
for(int cy = (int)(y - laserRange - 1); cy <= y + laserRange + 1; cy++){
|
for(int cy = (int)(y - laserRange - 1); cy <= y + laserRange + 1; cy++){
|
||||||
Tile link = world.ltile(cx, cy);
|
Tile link = world.ltile(cx, cy);
|
||||||
@@ -221,7 +235,7 @@ public class PowerNode extends PowerBlock{
|
|||||||
Drawf.square(link.drawx(), link.drawy(), link.block().size * tilesize / 2f + 2f, link.pos() == lastPlaced ? Pal.place : Pal.accent);
|
Drawf.square(link.drawx(), link.drawy(), link.block().size * tilesize / 2f + 2f, link.pos() == lastPlaced ? Pal.place : Pal.accent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ public class LiquidConverter extends GenericCrafter{
|
|||||||
hasLiquids = true;
|
hasLiquids = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean outputsItems(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(){
|
public void init(){
|
||||||
ConsumeLiquidBase cl = consumes.get(ConsumeType.liquid);
|
ConsumeLiquidBase cl = consumes.get(ConsumeType.liquid);
|
||||||
|
|||||||
@@ -27,13 +27,25 @@
|
|||||||
"com/sun/jndi",
|
"com/sun/jndi",
|
||||||
"com/sun/xml",
|
"com/sun/xml",
|
||||||
"com/sun/script",
|
"com/sun/script",
|
||||||
"sum/awt",
|
"sun/awt",
|
||||||
"sun/java2d",
|
"sun/java2d",
|
||||||
"sun/font",
|
"sun/font",
|
||||||
"sun/rmi",
|
"sun/rmi",
|
||||||
"sun/swing",
|
"sun/swing",
|
||||||
"java/util/stream",
|
"java/util/stream",
|
||||||
"com/sun/media",
|
"com/sun/media",
|
||||||
|
"sun/print",
|
||||||
|
"sun/awt",
|
||||||
|
"sun/instrument",
|
||||||
|
"sun/security/tools",
|
||||||
|
"sun/security/smartcardio",
|
||||||
|
"sun/audio",
|
||||||
|
"javax/xml",
|
||||||
|
"javax/sound",
|
||||||
|
"javax/script",
|
||||||
|
"java/beans",
|
||||||
|
"java/applet",
|
||||||
|
"java/rmi",
|
||||||
"com/sun/naming",
|
"com/sun/naming",
|
||||||
"java/awt",
|
"java/awt",
|
||||||
"com/sun/org/apache/xpath",
|
"com/sun/org/apache/xpath",
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
#convert ogg to .caf files for iOS
|
cd $1
|
||||||
for i in $1/*.ogg; do
|
|
||||||
|
#convert ogg to .mp3 files for iOS
|
||||||
|
for i in *.ogg; do
|
||||||
echo $i
|
echo $i
|
||||||
ffmpeg -i "$i" "${i%.*}.caf"
|
ffmpeg -i "$i" "OUT_${i%.*}.mp3"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
find . -type f ! -name "OUT_*" -delete
|
||||||
|
|
||||||
|
for file in OUT_*; do mv "$file" "${file#OUT_}"; done;
|
||||||
|
|
||||||
|
cd ../../
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 996 KiB After Width: | Height: | Size: 706 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 905 KiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 993 KiB After Width: | Height: | Size: 632 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 725 KiB |
|
Before Width: | Height: | Size: 890 KiB After Width: | Height: | Size: 598 KiB |
|
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 204 KiB |
@@ -40,7 +40,7 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||||||
@Override
|
@Override
|
||||||
public void shareFile(FileHandle file){
|
public void shareFile(FileHandle file){
|
||||||
Log.info("Attempting to share file " + file);
|
Log.info("Attempting to share file " + file);
|
||||||
FileHandle to = Core.files.absolute(getDocumentsDirectory()).child(file.name()/* + ".png"*/);
|
FileHandle to = Core.files.absolute(getDocumentsDirectory()).child(file.name());
|
||||||
file.copyTo(to);
|
file.copyTo(to);
|
||||||
|
|
||||||
NSURL url = new NSURL(to.file());
|
NSURL url = new NSURL(to.file());
|
||||||
@@ -53,7 +53,6 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginForceLandscape(){
|
public void beginForceLandscape(){
|
||||||
Log.info("begin force landscape");
|
|
||||||
forced = true;
|
forced = true;
|
||||||
UINavigationController.attemptRotationToDeviceOrientation();
|
UINavigationController.attemptRotationToDeviceOrientation();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.anuke.arc.*;
|
|||||||
import io.anuke.arc.collection.*;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.function.*;
|
import io.anuke.arc.function.*;
|
||||||
import io.anuke.arc.net.*;
|
import io.anuke.arc.net.*;
|
||||||
|
import io.anuke.arc.util.async.*;
|
||||||
import io.anuke.arc.util.pooling.*;
|
import io.anuke.arc.util.pooling.*;
|
||||||
import io.anuke.mindustry.net.Net.*;
|
import io.anuke.mindustry.net.Net.*;
|
||||||
import io.anuke.mindustry.net.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
@@ -84,7 +85,7 @@ public class ArcNetClient implements ClientProvider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(String ip, int port, Runnable success){
|
public void connect(String ip, int port, Runnable success){
|
||||||
runAsync(() -> {
|
Threads.daemon(() -> {
|
||||||
try{
|
try{
|
||||||
//just in case
|
//just in case
|
||||||
client.stop();
|
client.stop();
|
||||||
@@ -99,7 +100,7 @@ public class ArcNetClient implements ClientProvider{
|
|||||||
updateThread.setDaemon(true);
|
updateThread.setDaemon(true);
|
||||||
updateThread.start();
|
updateThread.start();
|
||||||
|
|
||||||
client.connect(5000, ip, port, port);
|
client.connect(5000, ip, port);
|
||||||
success.run();
|
success.run();
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
handleException(e);
|
handleException(e);
|
||||||
@@ -115,11 +116,7 @@ public class ArcNetClient implements ClientProvider{
|
|||||||
@Override
|
@Override
|
||||||
public void send(Object object, SendMode mode){
|
public void send(Object object, SendMode mode){
|
||||||
try{
|
try{
|
||||||
if(mode == SendMode.tcp){
|
client.sendTCP(object);
|
||||||
client.sendTCP(object);
|
|
||||||
}else{
|
|
||||||
client.sendUDP(object);
|
|
||||||
}
|
|
||||||
//sending things can cause an under/overflow, catch it and disconnect instead of crashing
|
//sending things can cause an under/overflow, catch it and disconnect instead of crashing
|
||||||
}catch(BufferOverflowException | BufferUnderflowException e){
|
}catch(BufferOverflowException | BufferUnderflowException e){
|
||||||
Net.showError(e);
|
Net.showError(e);
|
||||||
@@ -140,7 +137,7 @@ public class ArcNetClient implements ClientProvider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> invalid){
|
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> invalid){
|
||||||
runAsync(() -> {
|
Threads.daemon(() -> {
|
||||||
try{
|
try{
|
||||||
DatagramSocket socket = new DatagramSocket();
|
DatagramSocket socket = new DatagramSocket();
|
||||||
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
||||||
@@ -189,12 +186,6 @@ public class ArcNetClient implements ClientProvider{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAsync(Runnable run){
|
|
||||||
Thread thread = new Thread(run, "Client Async Run");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleException(Exception e){
|
private void handleException(Exception e){
|
||||||
if(e instanceof ArcNetException){
|
if(e instanceof ArcNetException){
|
||||||
Core.app.post(() -> Net.showError(new IOException("mismatch")));
|
Core.app.post(() -> Net.showError(new IOException("mismatch")));
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package io.anuke.mindustry.net;
|
package io.anuke.mindustry.net;
|
||||||
|
|
||||||
import com.dosse.upnp.*;
|
|
||||||
import io.anuke.arc.*;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.arc.collection.*;
|
|
||||||
import io.anuke.arc.net.*;
|
import io.anuke.arc.net.*;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.mindustry.*;
|
import io.anuke.arc.util.async.*;
|
||||||
import io.anuke.mindustry.net.Net.*;
|
import io.anuke.mindustry.net.Net.*;
|
||||||
import io.anuke.mindustry.net.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import net.jpountz.lz4.*;
|
import net.jpountz.lz4.*;
|
||||||
@@ -19,14 +17,10 @@ import static io.anuke.mindustry.Vars.*;
|
|||||||
|
|
||||||
public class ArcNetServer implements ServerProvider{
|
public class ArcNetServer implements ServerProvider{
|
||||||
final Server server;
|
final Server server;
|
||||||
final CopyOnWriteArrayList<KryoConnection> connections = new CopyOnWriteArrayList<>();
|
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
|
||||||
final CopyOnWriteArraySet<Integer> missing = new CopyOnWriteArraySet<>();
|
|
||||||
final Array<KryoConnection> array = new Array<>();
|
|
||||||
final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
|
final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
|
||||||
Thread serverThread;
|
Thread serverThread;
|
||||||
|
|
||||||
int lastconnection = 0;
|
|
||||||
|
|
||||||
public ArcNetServer(){
|
public ArcNetServer(){
|
||||||
server = new Server(4096 * 2, 4096, new PacketSerializer());
|
server = new Server(4096 * 2, 4096, new PacketSerializer());
|
||||||
server.setMulticast(multicastGroup, multicastPort);
|
server.setMulticast(multicastGroup, multicastPort);
|
||||||
@@ -42,7 +36,7 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
public void connected(Connection connection){
|
public void connected(Connection connection){
|
||||||
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
||||||
|
|
||||||
KryoConnection kn = new KryoConnection(lastconnection++, ip, connection);
|
ArcConnection kn = new ArcConnection(ip, connection);
|
||||||
|
|
||||||
Connect c = new Connect();
|
Connect c = new Connect();
|
||||||
c.id = kn.id;
|
c.id = kn.id;
|
||||||
@@ -56,7 +50,7 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnected(Connection connection){
|
public void disconnected(Connection connection){
|
||||||
KryoConnection k = getByKryoID(connection.getID());
|
ArcConnection k = getByKryoID(connection.getID());
|
||||||
if(k == null) return;
|
if(k == null) return;
|
||||||
|
|
||||||
Disconnect c = new Disconnect();
|
Disconnect c = new Disconnect();
|
||||||
@@ -70,7 +64,7 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void received(Connection connection, Object object){
|
public void received(Connection connection, Object object){
|
||||||
KryoConnection k = getByKryoID(connection.getID());
|
ArcConnection k = getByKryoID(connection.getID());
|
||||||
if(object instanceof FrameworkMessage || k == null) return;
|
if(object instanceof FrameworkMessage || k == null) return;
|
||||||
|
|
||||||
Core.app.post(() -> {
|
Core.app.post(() -> {
|
||||||
@@ -99,18 +93,14 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Array<KryoConnection> getConnections(){
|
public Iterable<ArcConnection> getConnections(){
|
||||||
array.clear();
|
return connections;
|
||||||
for(KryoConnection c : connections){
|
|
||||||
array.add(c);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KryoConnection getByID(int id){
|
public ArcConnection getByID(int id){
|
||||||
for(int i = 0; i < connections.size(); i++){
|
for(int i = 0; i < connections.size(); i++){
|
||||||
KryoConnection con = connections.get(i);
|
ArcConnection con = connections.get(i);
|
||||||
if(con.id == id){
|
if(con.id == id){
|
||||||
return con;
|
return con;
|
||||||
}
|
}
|
||||||
@@ -121,28 +111,14 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void host(int port) throws IOException{
|
public void host(int port) throws IOException{
|
||||||
//attempt to open default ports if they're not already open
|
|
||||||
//this only opens the default port due to security concerns (?)
|
|
||||||
if(port == Vars.port){
|
|
||||||
async(() -> {
|
|
||||||
try{
|
|
||||||
if(!UPnP.isMappedTCP(port)) UPnP.openPortTCP(port);
|
|
||||||
if(!UPnP.isMappedUDP(port)) UPnP.openPortUDP(port);
|
|
||||||
}catch(Throwable ignored){
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
lastconnection = 0;
|
|
||||||
connections.clear();
|
connections.clear();
|
||||||
missing.clear();
|
server.bind(port);
|
||||||
server.bind(port, port);
|
|
||||||
|
|
||||||
serverThread = new Thread(() -> {
|
serverThread = new Thread(() -> {
|
||||||
try{
|
try{
|
||||||
server.run();
|
server.run();
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
if(!(e instanceof ClosedSelectorException)) handleException(e);
|
if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e);
|
||||||
}
|
}
|
||||||
}, "Net Server");
|
}, "Net Server");
|
||||||
serverThread.setDaemon(true);
|
serverThread.setDaemon(true);
|
||||||
@@ -152,102 +128,12 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
@Override
|
@Override
|
||||||
public void close(){
|
public void close(){
|
||||||
connections.clear();
|
connections.clear();
|
||||||
lastconnection = 0;
|
Threads.daemon(server::stop);
|
||||||
|
|
||||||
async(server::stop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
ArcConnection getByKryoID(int id){
|
||||||
public void sendStream(int id, Streamable stream){
|
|
||||||
KryoConnection connection = getByID(id);
|
|
||||||
if(connection == null) return;
|
|
||||||
try{
|
|
||||||
|
|
||||||
if(connection.connection != null){
|
|
||||||
|
|
||||||
connection.connection.addListener(new InputStreamSender(stream.stream, 512){
|
|
||||||
int id;
|
|
||||||
|
|
||||||
protected void start(){
|
|
||||||
//send an object so the receiving side knows how to handle the following chunks
|
|
||||||
StreamBegin begin = new StreamBegin();
|
|
||||||
begin.total = stream.stream.available();
|
|
||||||
begin.type = Registrator.getID(stream.getClass());
|
|
||||||
connection.connection.sendTCP(begin);
|
|
||||||
id = begin.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object next(byte[] bytes){
|
|
||||||
StreamChunk chunk = new StreamChunk();
|
|
||||||
chunk.id = id;
|
|
||||||
chunk.data = bytes;
|
|
||||||
return chunk; //wrap the byte[] with an object so the receiving side knows how to handle it.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
int cid;
|
|
||||||
StreamBegin begin = new StreamBegin();
|
|
||||||
begin.total = stream.stream.available();
|
|
||||||
begin.type = Registrator.getID(stream.getClass());
|
|
||||||
connection.send(begin, SendMode.tcp);
|
|
||||||
cid = begin.id;
|
|
||||||
|
|
||||||
while(stream.stream.available() > 0){
|
|
||||||
byte[] bytes = new byte[Math.min(512, stream.stream.available())];
|
|
||||||
stream.stream.read(bytes);
|
|
||||||
|
|
||||||
StreamChunk chunk = new StreamChunk();
|
|
||||||
chunk.id = cid;
|
|
||||||
chunk.data = bytes;
|
|
||||||
connection.send(chunk, SendMode.tcp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(IOException e){
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(Object object, SendMode mode){
|
|
||||||
for(int i = 0; i < connections.size(); i++){
|
for(int i = 0; i < connections.size(); i++){
|
||||||
connections.get(i).send(object, mode);
|
ArcConnection con = connections.get(i);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendTo(int id, Object object, SendMode mode){
|
|
||||||
NetConnection conn = getByID(id);
|
|
||||||
if(conn == null){
|
|
||||||
if(!missing.contains(id))
|
|
||||||
Log.err("Failed to find connection with ID {0}.", id);
|
|
||||||
missing.add(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
conn.send(object, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendExcept(int id, Object object, SendMode mode){
|
|
||||||
for(int i = 0; i < connections.size(); i++){
|
|
||||||
KryoConnection conn = connections.get(i);
|
|
||||||
if(conn.id != id) conn.send(object, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose(){
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleException(Throwable e){
|
|
||||||
Time.run(0f, () -> {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
KryoConnection getByKryoID(int id){
|
|
||||||
for(int i = 0; i < connections.size(); i++){
|
|
||||||
KryoConnection con = connections.get(i);
|
|
||||||
if(con.connection != null && con.connection.getID() == id){
|
if(con.connection != null && con.connection.getID() == id){
|
||||||
return con;
|
return con;
|
||||||
}
|
}
|
||||||
@@ -256,17 +142,11 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void async(Runnable run){
|
class ArcConnection extends NetConnection{
|
||||||
Thread thread = new Thread(run);
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
class KryoConnection extends NetConnection{
|
|
||||||
public final Connection connection;
|
public final Connection connection;
|
||||||
|
|
||||||
public KryoConnection(int id, String address, Connection connection){
|
public ArcConnection(String address, Connection connection){
|
||||||
super(id, address);
|
super(address);
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,17 +158,13 @@ public class ArcNetServer implements ServerProvider{
|
|||||||
@Override
|
@Override
|
||||||
public void send(Object object, SendMode mode){
|
public void send(Object object, SendMode mode){
|
||||||
try{
|
try{
|
||||||
if(mode == SendMode.tcp){
|
connection.sendTCP(object);
|
||||||
connection.sendTCP(object);
|
|
||||||
}else{
|
|
||||||
connection.sendUDP(object);
|
|
||||||
}
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
Log.info("Error sending packet. Disconnecting invalid client!");
|
Log.info("Error sending packet. Disconnecting invalid client!");
|
||||||
connection.close();
|
connection.close();
|
||||||
|
|
||||||
KryoConnection k = getByKryoID(connection.getID());
|
ArcConnection k = getByKryoID(connection.getID());
|
||||||
if(k != null) connections.remove(k);
|
if(k != null) connections.remove(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
|
|
||||||
info("Selected next map to be {0}.", map.name());
|
info("Selected next map to be {0}.", map.name());
|
||||||
|
|
||||||
play(true, () -> world.loadMap(map));
|
play(true, () -> world.loadMap(map, map.applyRules(lastMode)));
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
netServer.kickAll(KickReason.gameover);
|
netServer.kickAll(KickReason.gameover);
|
||||||
@@ -231,7 +231,7 @@ public class ServerControl implements ApplicationListener{
|
|||||||
logic.reset();
|
logic.reset();
|
||||||
lastMode = preset;
|
lastMode = preset;
|
||||||
try{
|
try{
|
||||||
world.loadMap(result);
|
world.loadMap(result, result.applyRules(lastMode));
|
||||||
state.rules = result.applyRules(preset);
|
state.rules = result.applyRules(preset);
|
||||||
logic.play();
|
logic.play();
|
||||||
|
|
||||||
@@ -700,10 +700,11 @@ public class ServerControl implements ApplicationListener{
|
|||||||
run.run();
|
run.run();
|
||||||
logic.play();
|
logic.play();
|
||||||
state.rules = world.getMap().applyRules(lastMode);
|
state.rules = world.getMap().applyRules(lastMode);
|
||||||
|
|
||||||
for(Player p : players){
|
for(Player p : players){
|
||||||
p.reset();
|
p.reset();
|
||||||
if(state.rules.pvp){
|
if(state.rules.pvp){
|
||||||
p.setTeam(netServer.assignTeam(new ArrayIterable<>(players)));
|
p.setTeam(netServer.assignTeam(p, new ArrayIterable<>(players)));
|
||||||
}
|
}
|
||||||
netServer.sendWorldData(p, p.con.id);
|
netServer.sendWorldData(p, p.con.id);
|
||||||
}
|
}
|
||||||
|
|||||||