Extensive network refactor

This commit is contained in:
Anuken
2019-09-07 14:11:50 -04:00
parent 38fe99f10e
commit e90c8c4d3e
48 changed files with 781 additions and 887 deletions

View File

@@ -1,12 +1,11 @@
package io.anuke.mindustry; package io.anuke.mindustry;
import android.*;
import android.app.*; import android.app.*;
import android.content.*; import android.content.*;
import android.content.pm.*; import android.content.pm.*;
import android.net.*; import android.net.*;
import android.os.*;
import android.os.Build.*; import android.os.Build.*;
import android.os.*;
import android.provider.Settings.*; import android.provider.Settings.*;
import android.telephony.*; import android.telephony.*;
import io.anuke.arc.*; import io.anuke.arc.*;
@@ -18,13 +17,12 @@ import io.anuke.arc.util.*;
import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.game.Saves.*;
import io.anuke.mindustry.io.*; import io.anuke.mindustry.io.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import java.io.*; import java.io.*;
import java.lang.System; import java.lang.System;
import java.util.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -41,10 +39,13 @@ public class AndroidLauncher extends AndroidApplication{
UnitScl.dp.addition = 0.5f; UnitScl.dp.addition = 0.5f;
} }
Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new ArcNetServer());
initialize(new ClientLauncher(){ initialize(new ClientLauncher(){
@Override
public NetProvider getNet(){
return new ArcNetImpl();
}
@Override @Override
public void hide(){ public void hide(){
moveTaskToBack(true); moveTaskToBack(true);
@@ -68,24 +69,6 @@ public class AndroidLauncher extends AndroidApplication{
} }
} }
@Override
public void requestExternalPerms(Runnable callback){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){
callback.run();
}else{
permCallback = callback;
ArrayList<String> perms = new ArrayList<>();
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
perms.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}
}
@Override @Override
public void shareFile(FileHandle file){ public void shareFile(FileHandle file){
} }

View File

@@ -97,7 +97,7 @@ public class RemoteWriteGenerator{
if(!forwarded && methodEntry.local != Loc.none){ if(!forwarded && methodEntry.local != Loc.none){
//add in local checks //add in local checks
if(methodEntry.local != Loc.both){ if(methodEntry.local != Loc.both){
method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.net.Net.active())"); method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.Vars.net.active())");
} }
//concatenate parameters //concatenate parameters
@@ -159,7 +159,7 @@ public class RemoteWriteGenerator{
boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0; boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0;
if(writePlayerSkipCheck){ //write begin check if(writePlayerSkipCheck){ //write begin check
method.beginControlFlow("if(io.anuke.mindustry.net.Net.server())"); method.beginControlFlow("if(io.anuke.mindustry.Vars.net.server())");
} }
if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
@@ -205,7 +205,7 @@ public class RemoteWriteGenerator{
} }
//send the actual packet //send the actual packet
method.addStatement("io.anuke.mindustry.net.Net." + sendString + "packet, " + method.addStatement("io.anuke.mindustry.Vars.net." + sendString + "packet, " +
(methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")"); (methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")");
@@ -217,8 +217,8 @@ public class RemoteWriteGenerator{
} }
private String getCheckString(Loc loc){ private String getCheckString(Loc loc){
return loc.isClient && loc.isServer ? "io.anuke.mindustry.net.Net.server() || io.anuke.mindustry.net.Net.client()" : return loc.isClient && loc.isServer ? "io.anuke.mindustry.Vars.net.server() || io.anuke.mindustry.Vars.net.client()" :
loc.isClient ? "io.anuke.mindustry.net.Net.client()" : loc.isClient ? "io.anuke.mindustry.Vars.net.client()" :
loc.isServer ? "io.anuke.mindustry.net.Net.server()" : "false"; loc.isServer ? "io.anuke.mindustry.Vars.net.server()" : "false";
} }
} }

View File

@@ -13,6 +13,7 @@ import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.maps.*; import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.net.Net;
import static io.anuke.arc.Core.*; import static io.anuke.arc.Core.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -40,6 +41,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
assets = new AssetManager(); assets = new AssetManager();
assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader()); assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader());
atlas = TextureAtlas.blankAtlas(); atlas = TextureAtlas.blankAtlas();
Vars.net = new Net(platform.getNet());
UI.loadSystemCursors(); UI.loadSystemCursors();

View File

@@ -141,6 +141,7 @@ public class Vars implements Loadable{
/** list of all locales that can be switched to */ /** list of all locales that can be switched to */
public static Locale[] locales; public static Locale[] locales;
public static Net net;
public static ContentLoader content; public static ContentLoader content;
public static GameState state; public static GameState state;
public static GlobalData data; public static GlobalData data;
@@ -231,7 +232,7 @@ public class Vars implements Loadable{
for(EntityGroup<?> group : entities.all()){ for(EntityGroup<?> group : entities.all()){
group.setRemoveListener(entity -> { group.setRemoveListener(entity -> {
if(entity instanceof SyncTrait && Net.client()){ if(entity instanceof SyncTrait && net.client()){
netClient.addRemovedEntity((entity).getID()); netClient.addRemovedEntity((entity).getID());
} }
}); });

View File

@@ -25,7 +25,7 @@ public class Pathfinder{
public Pathfinder(){ public Pathfinder(){
Events.on(WorldLoadEvent.class, event -> clear()); Events.on(WorldLoadEvent.class, event -> clear());
Events.on(TileChangeEvent.class, event -> { Events.on(TileChangeEvent.class, event -> {
if(Net.client()) return; if(net.client()) return;
for(Team team : Team.all){ for(Team team : Team.all){
TeamData data = state.teams.get(team); TeamData data = state.teams.get(team);
@@ -43,7 +43,7 @@ public class Pathfinder{
} }
public void update(){ public void update(){
if(Net.client() || paths == null) return; if(net.client() || paths == null) return;
for(Team team : Team.all){ for(Team team : Team.all){
if(state.teams.isActive(team)){ if(state.teams.isActive(team)){

View File

@@ -116,7 +116,7 @@ public class WaveSpawner{
} }
public boolean isSpawning(){ public boolean isSpawning(){
return spawning && !Net.client(); return spawning && !net.client();
} }
private void reset(){ private void reset(){

View File

@@ -17,7 +17,6 @@ import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.input.*; import io.anuke.mindustry.input.*;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -29,6 +28,7 @@ import java.util.*;
import static io.anuke.arc.Core.*; import static io.anuke.arc.Core.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.net;
/** /**
* Control module. * Control module.
@@ -67,7 +67,7 @@ public class Control implements ApplicationListener, Loadable{
Events.on(WorldLoadEvent.class, event -> { Events.on(WorldLoadEvent.class, event -> {
Core.app.post(() -> Core.app.post(() -> { Core.app.post(() -> Core.app.post(() -> {
if(Net.active() && player.getClosestCore() != null){ if(net.active() && player.getClosestCore() != null){
//set to closest core since that's where the player will probably respawn; prevents camera jumps //set to closest core since that's where the player will probably respawn; prevents camera jumps
Core.camera.position.set(player.isDead() ? player.getClosestCore() : player); Core.camera.position.set(player.isDead() ? player.getClosestCore() : player);
}else{ }else{
@@ -99,7 +99,7 @@ public class Control implements ApplicationListener, Loadable{
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
//the restart dialog can show info for any number of scenarios //the restart dialog can show info for any number of scenarios
Call.onGameOver(event.winner); Call.onGameOver(event.winner);
if(state.rules.zone != null && !Net.client()){ if(state.rules.zone != null && !net.client()){
//remove zone save on game over //remove zone save on game over
if(saves.getZoneSlot() != null && !state.rules.tutorial){ if(saves.getZoneSlot() != null && !state.rules.tutorial){
saves.getZoneSlot().delete(); saves.getZoneSlot().delete();
@@ -109,9 +109,9 @@ public class Control implements ApplicationListener, Loadable{
//autohost for pvp maps //autohost for pvp maps
Events.on(WorldLoadEvent.class, event -> { Events.on(WorldLoadEvent.class, event -> {
if(state.rules.pvp && !Net.active()){ if(state.rules.pvp && !net.active()){
try{ try{
Net.host(port); net.host(port);
player.isAdmin = true; player.isAdmin = true;
}catch(IOException e){ }catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true))); ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
@@ -210,7 +210,7 @@ public class Control implements ApplicationListener, Loadable{
public void playZone(Zone zone){ public void playZone(Zone zone){
ui.loadAnd(() -> { ui.loadAnd(() -> {
logic.reset(); logic.reset();
Net.reset(); net.reset();
world.loadGenerator(zone.generator); world.loadGenerator(zone.generator);
zone.rules.accept(state.rules); zone.rules.accept(state.rules);
state.rules.zone = zone; state.rules.zone = zone;
@@ -229,7 +229,7 @@ public class Control implements ApplicationListener, Loadable{
Zone zone = Zones.groundZero; Zone zone = Zones.groundZero;
ui.loadAnd(() -> { ui.loadAnd(() -> {
logic.reset(); logic.reset();
Net.reset(); net.reset();
world.beginMapLoad(); world.beginMapLoad();
@@ -284,7 +284,7 @@ public class Control implements ApplicationListener, Loadable{
@Override @Override
public void dispose(){ public void dispose(){
content.dispose(); content.dispose();
Net.dispose(); net.dispose();
Musics.dispose(); Musics.dispose();
Sounds.dispose(); Sounds.dispose();
ui.editor.dispose(); ui.editor.dispose();

View File

@@ -1,14 +1,12 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import io.anuke.arc.Events; import io.anuke.arc.*;
import io.anuke.mindustry.entities.type.BaseUnit; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.type.base.BaseDrone; import io.anuke.mindustry.entities.type.base.*;
import io.anuke.mindustry.game.EventType.StateChangeEvent; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.net.Net;
import static io.anuke.mindustry.Vars.unitGroups; import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.waveTeam;
public class GameState{ public class GameState{
/** Current wave number, can be anything in non-wave modes. */ /** Current wave number, can be anything in non-wave modes. */
@@ -29,7 +27,7 @@ public class GameState{
private State state = State.menu; private State state = State.menu;
public int enemies(){ public int enemies(){
return Net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone)); return net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone));
} }
public BaseUnit boss(){ public BaseUnit boss(){
@@ -46,7 +44,7 @@ public class GameState{
} }
public boolean isPaused(){ public boolean isPaused(){
return (is(State.paused) && !Net.active()) || (gameOver && !Net.active()); return (is(State.paused) && !net.active()) || (gameOver && !net.active());
} }
public boolean is(State astate){ public boolean is(State astate){

View File

@@ -190,7 +190,7 @@ public class Logic implements ApplicationListener{
} }
} }
if(!Net.client() && state.wavetime <= 0 && state.rules.waves){ if(!net.client() && state.wavetime <= 0 && state.rules.waves){
runWave(); runWave();
} }
@@ -237,7 +237,7 @@ public class Logic implements ApplicationListener{
pathfinder.update(); pathfinder.update();
} }
if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){ if(!net.client() && !world.isInvalidMap() && !state.isEditor()){
checkGameOver(); checkGameOver();
} }
} }

View File

@@ -40,6 +40,7 @@ public class NetClient implements ApplicationListener{
private final static float playerSyncTime = 2; private final static float playerSyncTime = 2;
public final static float viewScale = 2f; public final static float viewScale = 2f;
private long ping;
private Interval timer = new Interval(5); private Interval timer = new Interval(5);
/** Whether the client is currently connecting. */ /** Whether the client is currently connecting. */
private boolean connecting = false; private boolean connecting = false;
@@ -60,7 +61,7 @@ public class NetClient implements ApplicationListener{
public NetClient(){ public NetClient(){
Net.handleClient(Connect.class, packet -> { net.handleClient(Connect.class, packet -> {
Log.info("Connecting to server: {0}", packet.addressTCP); Log.info("Connecting to server: {0}", packet.addressTCP);
player.isAdmin = false; player.isAdmin = false;
@@ -74,7 +75,7 @@ public class NetClient implements ApplicationListener{
ui.loadfrag.hide(); ui.loadfrag.hide();
connecting = false; connecting = false;
quiet = true; quiet = true;
Net.disconnect(); net.disconnect();
}); });
ConnectPacket c = new ConnectPacket(); ConnectPacket c = new ConnectPacket();
@@ -92,10 +93,10 @@ public class NetClient implements ApplicationListener{
return; return;
} }
Net.send(c, SendMode.tcp); net.send(c, SendMode.tcp);
}); });
Net.handleClient(Disconnect.class, packet -> { net.handleClient(Disconnect.class, packet -> {
if(quietReset) return; if(quietReset) return;
connecting = false; connecting = false;
@@ -120,14 +121,14 @@ public class NetClient implements ApplicationListener{
} }
}); });
Net.handleClient(WorldStream.class, data -> { net.handleClient(WorldStream.class, data -> {
Log.info("Recieved world data: {0} bytes.", data.stream.available()); Log.info("Recieved world data: {0} bytes.", data.stream.available());
NetworkIO.loadWorld(new InflaterInputStream(data.stream)); NetworkIO.loadWorld(new InflaterInputStream(data.stream));
finishConnecting(); finishConnecting();
}); });
Net.handleClient(InvokePacket.class, packet -> { net.handleClient(InvokePacket.class, packet -> {
packet.writeBuffer.position(0); packet.writeBuffer.position(0);
RemoteReadClient.readPacket(packet.writeBuffer, packet.type); RemoteReadClient.readPacket(packet.writeBuffer, packet.type);
}); });
@@ -198,6 +199,16 @@ public class NetClient implements ApplicationListener{
return "[#" + player.color.toString().toUpperCase() + "]" + name; return "[#" + player.color.toString().toUpperCase() + "]" + name;
} }
@Remote(targets = Loc.client)
public static void onPing(Player player, long time){
Call.onPingResponse(player.id, time);
}
@Remote(variants = Variant.one)
public static void onPingResponse(long time){
netClient.ping = Time.timeSinceMillis(time);
}
@Remote(variants = Variant.one) @Remote(variants = Variant.one)
public static void onTraceInfo(Player player, TraceInfo info){ public static void onTraceInfo(Player player, TraceInfo info){
if(player != null){ if(player != null){
@@ -233,7 +244,7 @@ public class NetClient implements ApplicationListener{
logic.reset(); logic.reset();
ui.chatfrag.clearMessages(); ui.chatfrag.clearMessages();
Net.setClientLoaded(false); net.setClientLoaded(false);
ui.loadfrag.show("$connecting.data"); ui.loadfrag.show("$connecting.data");
@@ -241,7 +252,7 @@ public class NetClient implements ApplicationListener{
ui.loadfrag.hide(); ui.loadfrag.hide();
netClient.connecting = false; netClient.connecting = false;
netClient.quiet = true; netClient.quiet = true;
Net.disconnect(); net.disconnect();
}); });
} }
@@ -259,7 +270,7 @@ public class NetClient implements ApplicationListener{
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){ public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){
try{ try{
netClient.byteStream.setBytes(Net.decompressSnapshot(data, dataLen)); netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen));
DataInputStream input = netClient.dataStream; DataInputStream input = netClient.dataStream;
EntityGroup group = entities.get(groupID); EntityGroup group = entities.get(groupID);
@@ -315,7 +326,7 @@ public class NetClient implements ApplicationListener{
state.wave = wave; state.wave = wave;
state.enemies = enemies; state.enemies = enemies;
netClient.byteStream.setBytes(Net.decompressSnapshot(coreData, coreDataLen)); netClient.byteStream.setBytes(net.decompressSnapshot(coreData, coreDataLen));
DataInputStream input = netClient.dataStream; DataInputStream input = netClient.dataStream;
byte cores = input.readByte(); byte cores = input.readByte();
@@ -337,12 +348,12 @@ public class NetClient implements ApplicationListener{
@Override @Override
public void update(){ public void update(){
if(!Net.client()) return; if(!net.client()) return;
if(!state.is(State.menu)){ if(!state.is(State.menu)){
if(!connecting) sync(); if(!connecting) sync();
}else if(!connecting){ }else if(!connecting){
Net.disconnect(); net.disconnect();
}else{ //...must be connecting }else{ //...must be connecting
timeoutTime += Time.delta(); timeoutTime += Time.delta();
if(timeoutTime > dataTimeout){ if(timeoutTime > dataTimeout){
@@ -350,7 +361,7 @@ public class NetClient implements ApplicationListener{
ui.loadfrag.hide(); ui.loadfrag.hide();
quiet = true; quiet = true;
ui.showError("$disconnect.data"); ui.showError("$disconnect.data");
Net.disconnect(); net.disconnect();
timeoutTime = 0f; timeoutTime = 0f;
} }
} }
@@ -360,18 +371,22 @@ public class NetClient implements ApplicationListener{
return connecting; return connecting;
} }
public int getPing(){
return (int)ping;
}
private void finishConnecting(){ private void finishConnecting(){
state.set(State.playing); state.set(State.playing);
connecting = false; connecting = false;
ui.join.hide(); ui.join.hide();
Net.setClientLoaded(true); net.setClientLoaded(true);
Core.app.post(Call::connectConfirm); Core.app.post(Call::connectConfirm);
Time.runTask(40f, platform::updateRPC); Time.runTask(40f, platform::updateRPC);
Core.app.post(() -> ui.loadfrag.hide()); Core.app.post(() -> ui.loadfrag.hide());
} }
private void reset(){ private void reset(){
Net.setClientLoaded(false); net.setClientLoaded(false);
removed.clear(); removed.clear();
timeoutTime = 0f; timeoutTime = 0f;
connecting = true; connecting = true;
@@ -390,13 +405,13 @@ public class NetClient implements ApplicationListener{
/** Disconnects, resetting state to the menu. */ /** Disconnects, resetting state to the menu. */
public void disconnectQuietly(){ public void disconnectQuietly(){
quiet = true; quiet = true;
Net.disconnect(); net.disconnect();
} }
/** Disconnects, causing no further changes or reset.*/ /** Disconnects, causing no further changes or reset.*/
public void disconnectNoReset(){ public void disconnectNoReset(){
quiet = quietReset = true; quiet = quietReset = true;
Net.disconnect(); net.disconnect();
} }
/** When set, any disconnects will be ignored and no dialogs will be shown. */ /** When set, any disconnects will be ignored and no dialogs will be shown. */
@@ -435,7 +450,7 @@ public class NetClient implements ApplicationListener{
} }
if(timer.get(1, 60)){ if(timer.get(1, 60)){
Net.updatePing(); Call.onPing(Time.millis());
} }
} }

View File

@@ -19,7 +19,6 @@ import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Administration.*;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -41,8 +40,6 @@ public class NetServer implements ApplicationListener{
public final Administration admins = new Administration(); public final Administration admins = new Administration();
public final CommandHandler clientCommands = new CommandHandler("/"); public final CommandHandler clientCommands = new CommandHandler("/");
/** Maps connection IDs to players. */
private IntMap<Player> connections = new IntMap<>();
private boolean closing = false; private boolean closing = false;
private ByteBuffer writeBuffer = ByteBuffer.allocate(127); private ByteBuffer writeBuffer = ByteBuffer.allocate(127);
@@ -54,63 +51,51 @@ public class NetServer implements ApplicationListener{
private DataOutputStream dataStream = new DataOutputStream(syncStream); private DataOutputStream dataStream = new DataOutputStream(syncStream);
public NetServer(){ public NetServer(){
Events.on(WorldLoadEvent.class, event -> {
if(!headless){
connections.clear();
}
});
Net.handleServer(Connect.class, (id, connect) -> { net.handleServer(Connect.class, (con, connect) -> {
if(admins.isIPBanned(connect.addressTCP)){ if(admins.isIPBanned(connect.addressTCP)){
kick(id, KickReason.banned); con.kick(KickReason.banned);
} }
}); });
Net.handleServer(Disconnect.class, (id, packet) -> { net.handleServer(Disconnect.class, (con, packet) -> {
Player player = connections.get(id); if(con.player != null){
if(player != null){
onDisconnect(player, packet.reason); onDisconnect(player, packet.reason);
} }
connections.remove(id);
}); });
Net.handleServer(ConnectPacket.class, (id, packet) -> { net.handleServer(ConnectPacket.class, (con, packet) -> {
String uuid = packet.uuid; String uuid = packet.uuid;
NetConnection connection = Net.getConnection(id); if(admins.isIPBanned(con.address)) return;
Log.info("\n\nGET CONNECT\n\n");
if(connection == null || if(con.hasBegunConnecting){
admins.isIPBanned(connection.address)) return; con.kick(KickReason.idInUse);
if(connection.hasBegunConnecting){
kick(id, KickReason.idInUse);
return; return;
} }
connection.hasBegunConnecting = true;
PlayerInfo info = admins.getInfo(uuid); PlayerInfo info = admins.getInfo(uuid);
connection.mobile = packet.mobile; con.hasBegunConnecting = true;
con.mobile = packet.mobile;
if(admins.isIDBanned(uuid)){ if(admins.isIDBanned(uuid)){
kick(id, KickReason.banned); con.kick(KickReason.banned);
return; return;
} }
if(Time.millis() - info.lastKicked < kickDuration){ if(Time.millis() - info.lastKicked < kickDuration){
kick(id, KickReason.recentKick); con.kick(KickReason.recentKick);
return; return;
} }
if(admins.isIDBanned(uuid)){ if(admins.isIDBanned(uuid)){
kick(id, KickReason.banned); con.kick(KickReason.banned);
return; return;
} }
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){ if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
kick(id, KickReason.playerLimit); con.kick(KickReason.playerLimit);
return; return;
} }
@@ -119,14 +104,14 @@ public class NetServer implements ApplicationListener{
info.lastName = packet.name; info.lastName = packet.name;
info.id = packet.uuid; info.id = packet.uuid;
admins.save(); admins.save();
Call.onInfoMessage(id, "You are not whitelisted here."); Call.onInfoMessage(con.id, "You are not whitelisted here.");
Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name); Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name);
kick(id, KickReason.whitelist); con.kick(KickReason.whitelist);
return; return;
} }
if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){ if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){
kick(id, !Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient); con.kick(!Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient);
return; return;
} }
@@ -135,12 +120,12 @@ public class NetServer implements ApplicationListener{
if(preventDuplicates){ if(preventDuplicates){
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
if(player.name.trim().equalsIgnoreCase(packet.name.trim())){ if(player.name.trim().equalsIgnoreCase(packet.name.trim())){
kick(id, KickReason.nameInUse); con.kick(KickReason.nameInUse);
return; return;
} }
if(player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid)){ if(player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid)){
kick(id, KickReason.idInUse); con.kick(KickReason.idInUse);
return; return;
} }
} }
@@ -149,26 +134,26 @@ public class NetServer implements ApplicationListener{
packet.name = fixName(packet.name); packet.name = fixName(packet.name);
if(packet.name.trim().length() <= 0){ if(packet.name.trim().length() <= 0){
kick(id, KickReason.nameEmpty); con.kick(KickReason.nameEmpty);
return; return;
} }
String ip = Net.getConnection(id).address; String ip = con.address;
admins.updatePlayerJoined(uuid, ip, packet.name); admins.updatePlayerJoined(uuid, ip, packet.name);
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){ if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
kick(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated); con.kick(packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
return; return;
} }
if(packet.version == -1){ if(packet.version == -1){
connection.modclient = true; con.modclient = true;
} }
Player player = new Player(); Player player = new Player();
player.isAdmin = admins.isAdmin(uuid, packet.usid); player.isAdmin = admins.isAdmin(uuid, packet.usid);
player.con = Net.getConnection(id); player.con = con;
player.usid = packet.usid; player.usid = packet.usid;
player.name = packet.name; player.name = packet.name;
player.uuid = uuid; player.uuid = uuid;
@@ -183,29 +168,28 @@ public class NetServer implements ApplicationListener{
player.write(outputBuffer); player.write(outputBuffer);
}catch(Throwable t){ }catch(Throwable t){
t.printStackTrace(); t.printStackTrace();
kick(id, KickReason.nameEmpty); con.kick(KickReason.nameEmpty);
return; return;
} }
con.player = player;
//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(player, 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());
} }
connections.put(id, player); sendWorldData(player, con.id);
sendWorldData(player, id);
platform.updateRPC(); platform.updateRPC();
Events.fire(new PlayerJoin(player)); Events.fire(new PlayerJoin(player));
}); });
Net.handleServer(InvokePacket.class, (id, packet) -> { net.handleServer(InvokePacket.class, (con, packet) -> {
Player player = connections.get(id); if(con.player == null) return;
if(player == null) return; RemoteReadServer.readPacket(packet.writeBuffer, packet.type, con.player);
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player);
}); });
registerCommands(); registerCommands();
@@ -277,7 +261,7 @@ public class NetServer implements ApplicationListener{
if(votes >= votesRequired() && target.isAdded() && target.con.isConnected()){ if(votes >= votesRequired() && target.isAdded() && target.con.isConnected()){
Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be kicked from the server.", target.name)); Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be kicked from the server.", target.name));
admins.getInfo(target.uuid).lastKicked = Time.millis() + kickDuration*1000; admins.getInfo(target.uuid).lastKicked = Time.millis() + kickDuration*1000;
kick(target.con.id, KickReason.vote); target.con.kick(KickReason.vote);
map[0] = null; map[0] = null;
task.cancel(); task.cancel();
return true; return true;
@@ -404,7 +388,7 @@ public class NetServer implements ApplicationListener{
NetworkIO.writeWorld(player, def); NetworkIO.writeWorld(player, def);
WorldStream data = new WorldStream(); WorldStream data = new WorldStream();
data.stream = new ByteArrayInputStream(stream.toByteArray()); data.stream = new ByteArrayInputStream(stream.toByteArray());
Net.sendStream(clientID, data); net.sendStream(clientID, data);
Log.debug("Packed {0} compressed bytes of world data.", stream.size()); Log.debug("Packed {0} compressed bytes of world data.", stream.size());
} }
@@ -422,7 +406,6 @@ public class NetServer implements ApplicationListener{
Call.onPlayerDisconnect(player.id); Call.onPlayerDisconnect(player.id);
} }
player.remove(); player.remove();
netServer.connections.remove(player.con.id);
Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason); Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
} }
@@ -539,10 +522,10 @@ public class NetServer implements ApplicationListener{
state.wavetime = 0f; state.wavetime = 0f;
}else if(action == AdminAction.ban){ }else if(action == AdminAction.ban){
netServer.admins.banPlayerIP(other.con.address); netServer.admins.banPlayerIP(other.con.address);
netServer.kick(other.con.id, KickReason.banned); other.con.kick(KickReason.banned);
Log.info("&lc{0} has banned {1}.", player.name, other.name); Log.info("&lc{0} has banned {1}.", player.name, other.name);
}else if(action == AdminAction.kick){ }else if(action == AdminAction.kick){
netServer.kick(other.con.id, KickReason.kick); other.con.kick(KickReason.kick);
Log.info("&lc{0} has kicked {1}.", player.name, other.name); Log.info("&lc{0} has kicked {1}.", player.name, other.name);
}else if(action == AdminAction.trace){ }else if(action == AdminAction.trace){
TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile); TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile);
@@ -580,51 +563,27 @@ public class NetServer implements ApplicationListener{
public void update(){ public void update(){
if(!headless && !closing && Net.server() && state.is(State.menu)){ if(!headless && !closing && net.server() && state.is(State.menu)){
closing = true; closing = true;
ui.loadfrag.show("$server.closing"); ui.loadfrag.show("$server.closing");
Time.runTask(5f, () -> { Time.runTask(5f, () -> {
Net.closeServer(); net.closeServer();
ui.loadfrag.hide(); ui.loadfrag.hide();
closing = false; closing = false;
}); });
} }
if(!state.is(State.menu) && Net.server()){ if(!state.is(State.menu) && net.server()){
sync(); sync();
} }
} }
public void kickAll(KickReason reason){ public void kickAll(KickReason reason){
for(NetConnection con : Net.getConnections()){ for(NetConnection con : net.getConnections()){
kick(con.id, reason); con.kick(reason);
} }
} }
public void kick(int connection, KickReason reason){
NetConnection con = Net.getConnection(connection);
if(con == null){
Log.err("Cannot kick unknown player!");
return;
}else{
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason.name());
}
Player player = connections.get(con.id);
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
PlayerInfo info = admins.getInfo(player.uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
}
Call.onKick(connection, reason);
Time.runTask(2f, con::close);
admins.save();
}
public void writeSnapshot(Player player) throws IOException{ public void writeSnapshot(Player player) throws IOException{
syncStream.reset(); syncStream.reset();
ObjectSet<Tile> cores = state.teams.get(player.getTeam()).cores; ObjectSet<Tile> cores = state.teams.get(player.getTeam()).cores;
@@ -640,7 +599,7 @@ public class NetServer implements ApplicationListener{
byte[] stateBytes = syncStream.toByteArray(); byte[] stateBytes = syncStream.toByteArray();
//write basic state data. //write basic state data.
Call.onStateSnapshot(player.con.id, state.wavetime, state.wave, state.enemies(), (short)stateBytes.length, Net.compressSnapshot(stateBytes)); Call.onStateSnapshot(player.con.id, state.wavetime, state.wave, state.enemies(), (short)stateBytes.length, net.compressSnapshot(stateBytes));
viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY); viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY);
@@ -671,7 +630,7 @@ public class NetServer implements ApplicationListener{
if(syncStream.size() > maxSnapshotSize){ if(syncStream.size() > maxSnapshotSize){
dataStream.close(); dataStream.close();
byte[] syncBytes = syncStream.toByteArray(); byte[] syncBytes = syncStream.toByteArray();
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
sent = 0; sent = 0;
syncStream.reset(); syncStream.reset();
} }
@@ -681,7 +640,7 @@ public class NetServer implements ApplicationListener{
dataStream.close(); dataStream.close();
byte[] syncBytes = syncStream.toByteArray(); byte[] syncBytes = syncStream.toByteArray();
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
} }
} }
} }
@@ -747,7 +706,7 @@ public class NetServer implements ApplicationListener{
NetConnection connection = player.con; NetConnection connection = player.con;
if(connection == null || !connection.isConnected() || !connections.containsKey(connection.id)){ if(connection == null || !connection.isConnected()){
//player disconnected, call d/c event //player disconnected, call d/c event
onDisconnect(player, "disappeared"); onDisconnect(player, "disappeared");
return; return;

View File

@@ -7,13 +7,17 @@ import io.anuke.arc.function.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import static io.anuke.mindustry.Vars.mobile; import static io.anuke.mindustry.Vars.mobile;
public interface Platform{ public interface Platform{
/**Steam: Update lobby visibility.*/ /** Get the networking implementation.*/
NetProvider getNet();
/** Steam: Update lobby visibility.*/
default void updateLobby(){ default void updateLobby(){
} }
@@ -41,11 +45,6 @@ public interface Platform{
}); });
} }
/** Request external read/write perms. Run callback when complete.*/
default void requestExternalPerms(Runnable callback){
callback.run();
}
/** Update discord RPC. */ /** Update discord RPC. */
default void updateRPC(){ default void updateRPC(){
} }

View File

@@ -40,7 +40,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
/** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */ /** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */
public static void create(Tile tile){ public static void create(Tile tile){
if(Net.client() || tile == null) return; //not clientside. if(net.client() || tile == null) return; //not clientside.
Fire fire = map.get(tile.pos()); Fire fire = map.get(tile.pos());
@@ -106,7 +106,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
time = Mathf.clamp(time + Time.delta(), 0, lifetime()); time = Mathf.clamp(time + Time.delta(), 0, lifetime());
map.put(tile.pos(), this); map.put(tile.pos(), this);
if(Net.client()){ if(net.client()){
return; return;
} }

View File

@@ -83,7 +83,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
Puddle p = map.get(tile.pos()); Puddle p = map.get(tile.pos());
if(p == null){ if(p == null){
if(Net.client()) return; //not clientside. if(net.client()) return; //not clientside.
Puddle puddle = Pools.obtain(Puddle.class, Puddle::new); Puddle puddle = Pools.obtain(Puddle.class, Puddle::new);
puddle.tile = tile; puddle.tile = tile;
@@ -168,7 +168,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
public void update(){ public void update(){
//no updating happens clientside //no updating happens clientside
if(Net.client()){ if(net.client()){
amount = Mathf.lerpDelta(amount, targetAmount, 0.15f); amount = Mathf.lerpDelta(amount, targetAmount, 0.15f);
}else{ }else{
//update code //update code

View File

@@ -48,7 +48,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public static void onUnitDeath(BaseUnit unit){ public static void onUnitDeath(BaseUnit unit){
if(unit == null) return; if(unit == null) return;
if(Net.server() || !Net.active()){ if(net.server() || !net.active()){
UnitDrops.dropItems(unit); UnitDrops.dropItems(unit);
} }
@@ -56,7 +56,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
unit.type.deathSound.at(unit); unit.type.deathSound.at(unit);
//visual only. //visual only.
if(Net.client()){ if(net.client()){
Tile tile = world.tile(unit.spawner); Tile tile = world.tile(unit.spawner);
if(tile != null){ if(tile != null){
tile.block().unitRemoved(tile, unit); tile.block().unitRemoved(tile, unit);
@@ -255,7 +255,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
hitTime -= Time.delta(); hitTime -= Time.delta();
if(Net.client()){ if(net.client()){
interpolate(); interpolate();
status.update(this); status.update(this);
return; return;
@@ -297,7 +297,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public void removed(){ public void removed(){
super.removed(); super.removed();
Tile tile = world.tile(spawner); Tile tile = world.tile(spawner);
if(tile != null && !Net.client()){ if(tile != null && !net.client()){
tile.block().unitRemoved(tile, this); tile.block().unitRemoved(tile, this);
} }

View File

@@ -123,7 +123,7 @@ public abstract class FlyingUnit extends BaseUnit{
public void update(){ public void update(){
super.update(); super.update();
if(!Net.client()){ if(!net.client()){
updateRotation(); updateRotation();
} }
wobble(); wobble();
@@ -176,7 +176,7 @@ public abstract class FlyingUnit extends BaseUnit{
} }
protected void wobble(){ protected void wobble(){
if(Net.client()) return; if(net.client()) return;
x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta();
y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta();

View File

@@ -1,34 +1,30 @@
package io.anuke.mindustry.entities.type; package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.*;
import io.anuke.annotations.Annotations.Remote; import io.anuke.arc.*;
import io.anuke.arc.Core; import io.anuke.arc.collection.*;
import io.anuke.arc.collection.Queue; 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.Angles; import io.anuke.arc.math.*;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.*; import io.anuke.arc.math.geom.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools; import io.anuke.arc.util.pooling.*;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.*;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.input.*; import io.anuke.mindustry.input.*;
import io.anuke.mindustry.input.InputHandler.PlaceDraw; import io.anuke.mindustry.input.InputHandler.*;
import io.anuke.mindustry.io.TypeIO; import io.anuke.mindustry.io.*;
import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Administration.*;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.NetConnection;
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.Tile;
import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.blocks.*;
import java.io.*; import java.io.*;
@@ -50,7 +46,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public float baseRotation; public float baseRotation;
public float pointerX, pointerY; public float pointerX, pointerY;
public String name = "name"; public String name = "name";
public String uuid, usid; public @Nullable String uuid, usid;
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping; public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping;
public float boostHeat, shootHeat, destructTime; public float boostHeat, shootHeat, destructTime;
public boolean achievedFlight; public boolean achievedFlight;
@@ -59,13 +55,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public SpawnerTrait spawner, lastSpawner; public SpawnerTrait spawner, lastSpawner;
public int respawns; public int respawns;
public NetConnection con; public @Nullable NetConnection con;
public boolean isLocal = false; public boolean isLocal = false;
public Interval timer = new Interval(6); public Interval timer = new Interval(6);
public TargetTrait target; public TargetTrait target;
public TargetTrait moveTarget; public TargetTrait moveTarget;
public String lastText; public @Nullable String lastText;
public float textFadeTime; public float textFadeTime;
private float walktime, itemtime; private float walktime, itemtime;
@@ -229,7 +225,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
@Override @Override
public void damage(float amount){ public void damage(float amount){
hitTime = hitDuration; hitTime = hitDuration;
if(!Net.client()){ if(!net.client()){
health -= calculateDamage(amount); health -= calculateDamage(amount);
} }
@@ -530,7 +526,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
spawner = null; spawner = null;
} }
if(isLocal || Net.server()){ if(isLocal || net.server()){
avoidOthers(); avoidOthers();
} }
@@ -561,7 +557,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
status.update(this); //status effect updating also happens with non locals for effect purposes status.update(this); //status effect updating also happens with non locals for effect purposes
updateVelocityStatus(); //velocity too, for visual purposes updateVelocityStatus(); //velocity too, for visual purposes
if(Net.server()){ if(net.server()){
updateShooting(); //server simulates player shooting updateShooting(); //server simulates player shooting
} }
return; return;
@@ -847,7 +843,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
}else if(spawner != null && spawner.isValid()){ }else if(spawner != null && spawner.isValid()){
spawner.updateSpawning(this); spawner.updateSpawning(this);
}else if(!netServer.isWaitingForPlayers()){ }else if(!netServer.isWaitingForPlayers()){
if(!Net.client()){ if(!net.client()){
if(lastSpawner != null && lastSpawner.isValid()){ if(lastSpawner != null && lastSpawner.isValid()){
this.spawner = lastSpawner; this.spawner = lastSpawner;
}else if(getClosestCore() != null){ }else if(getClosestCore() != null){

View File

@@ -78,7 +78,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
@Override @Override
public void damage(float amount){ public void damage(float amount){
if(!Net.client()){ if(!net.client()){
super.damage(calculateDamage(amount)); super.damage(calculateDamage(amount));
} }
hitTime = hitDuration; hitTime = hitDuration;
@@ -308,7 +308,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
drownTime = Mathf.clamp(drownTime); drownTime = Mathf.clamp(drownTime);
if(drownTime >= 0.999f && !Net.client()){ if(drownTime >= 0.999f && !net.client()){
damage(health + 1); damage(health + 1);
} }
@@ -347,7 +347,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
} }
public void applyEffect(StatusEffect effect, float duration){ public void applyEffect(StatusEffect effect, float duration){
if(dead || Net.client()) return; //effects are synced and thus not applied through clients if(dead || net.client()) return; //effects are synced and thus not applied through clients
status.handleApply(this, effect, duration); status.handleApply(this, effect, duration);
} }

View File

@@ -123,7 +123,7 @@ public class DesktopInput extends InputHandler{
@Override @Override
public void update(){ public void update(){
if(Net.active() && Core.input.keyTap(Binding.player_list)){ if(net.active() && Core.input.keyTap(Binding.player_list)){
ui.listfrag.toggle(); ui.listfrag.toggle();
} }

View File

@@ -49,7 +49,7 @@ public abstract class InputHandler implements InputProcessor{
@Remote(targets = Loc.client, called = Loc.server) @Remote(targets = Loc.client, called = Loc.server)
public static void dropItem(Player player, float angle){ public static void dropItem(Player player, float angle){
if(Net.server() && player.item().amount <= 0){ if(net.server() && player.item().amount <= 0){
throw new ValidateException(player, "Player cannot drop an item."); throw new ValidateException(player, "Player cannot drop an item.");
} }
@@ -60,7 +60,7 @@ public abstract class InputHandler implements InputProcessor{
@Remote(targets = Loc.both, forward = true, called = Loc.server) @Remote(targets = Loc.both, forward = true, called = Loc.server)
public static void transferInventory(Player player, Tile tile){ public static void transferInventory(Player player, Tile tile){
if(!player.timer.get(Player.timerTransfer, 40)) return; if(!player.timer.get(Player.timerTransfer, 40)) return;
if(Net.server() && (player.item().amount <= 0 || player.isTransferring)){ if(net.server() && (player.item().amount <= 0 || player.isTransferring)){
throw new ValidateException(player, "Player cannot transfer an item."); throw new ValidateException(player, "Player cannot transfer an item.");
} }

View File

@@ -17,6 +17,7 @@ import java.nio.file.Files;
import java.nio.file.*; import java.nio.file.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import static io.anuke.mindustry.Vars.*;
public class CrashSender{ public class CrashSender{
@@ -78,9 +79,9 @@ public class CrashSender{
//attempt to close connections, if applicable //attempt to close connections, if applicable
try{ try{
netActive = Net.active(); netActive = net.active();
netServer = Net.server(); netServer = net.server();
Net.dispose(); net.dispose();
}catch(Throwable ignored){ }catch(Throwable ignored){
} }

View File

@@ -17,20 +17,25 @@ import static io.anuke.mindustry.Vars.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class Net{ public class Net{
private static boolean server; private boolean server;
private static boolean active; private boolean active;
private static boolean clientLoaded; private boolean clientLoaded;
private static Array<Object> packetQueue = new Array<>();
private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>(); private final Array<Object> packetQueue = new Array<>();
private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>(); private final ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>();
private static ClientProvider clientProvider; private final ObjectMap<Class<?>, BiConsumer<NetConnection, Object>> serverListeners = new ObjectMap<>();
private static ServerProvider serverProvider; private final IntMap<StreamBuilder> streams = new IntMap<>();
private static IntMap<StreamBuilder> streams = new IntMap<>();
private static final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor(); private final NetProvider provider;
private static final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor(); private final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
private final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
public Net(NetProvider provider){
this.provider = provider;
}
/** Display a network error. Call on the graphics thread. */ /** Display a network error. Call on the graphics thread. */
public static void showError(Throwable e){ public void showError(Throwable e){
if(!headless){ if(!headless){
@@ -67,7 +72,7 @@ public class Net{
} }
ui.loadfrag.hide(); ui.loadfrag.hide();
if(Net.client()){ if(client()){
netClient.disconnectQuietly(); netClient.disconnectQuietly();
} }
} }
@@ -78,7 +83,7 @@ public class Net{
/** /**
* Sets the client loaded status, or whether it will recieve normal packets from the server. * Sets the client loaded status, or whether it will recieve normal packets from the server.
*/ */
public static void setClientLoaded(boolean loaded){ public void setClientLoaded(boolean loaded){
clientLoaded = loaded; clientLoaded = loaded;
if(loaded){ if(loaded){
@@ -91,7 +96,7 @@ public class Net{
packetQueue.clear(); packetQueue.clear();
} }
public static void setClientConnected(){ public void setClientConnected(){
active = true; active = true;
server = false; server = false;
} }
@@ -99,10 +104,10 @@ public class Net{
/** /**
* Connect to an address. * Connect to an address.
*/ */
public static void connect(String ip, int port, Runnable success){ public void connect(String ip, int port, Runnable success){
try{ try{
if(!active){ if(!active){
clientProvider.connect(ip, port, success); provider.connectClient(ip, port, success);
active = true; active = true;
server = false; server = false;
}else{ }else{
@@ -116,8 +121,8 @@ public class Net{
/** /**
* Host a server at an address. * Host a server at an address.
*/ */
public static void host(int port) throws IOException{ public void host(int port) throws IOException{
serverProvider.host(port); provider.hostServer(port);
active = true; active = true;
server = true; server = true;
@@ -127,32 +132,32 @@ public class Net{
/** /**
* Closes the server. * Closes the server.
*/ */
public static void closeServer(){ public void closeServer(){
for(NetConnection con : getConnections()){ for(NetConnection con : getConnections()){
Call.onKick(con.id, KickReason.serverClose); Call.onKick(con.id, KickReason.serverClose);
} }
serverProvider.close(); provider.closeServer();
server = false; server = false;
active = false; active = false;
} }
public static void reset(){ public void reset(){
closeServer(); closeServer();
netClient.disconnectNoReset(); netClient.disconnectNoReset();
} }
public static void disconnect(){ public void disconnect(){
clientProvider.disconnect(); provider.disconnectClient();
server = false; server = false;
active = false; active = false;
} }
public static byte[] compressSnapshot(byte[] input){ public byte[] compressSnapshot(byte[] input){
return compressor.compress(input); return compressor.compress(input);
} }
public static byte[] decompressSnapshot(byte[] input, int size){ public byte[] decompressSnapshot(byte[] input, int size){
return decompressor.decompress(input, size); return decompressor.decompress(input, size);
} }
@@ -160,88 +165,74 @@ public class Net{
* Starts discovering servers on a different thread. * Starts discovering servers on a different thread.
* Callback is run on the main libGDX thread. * Callback is run on the main libGDX thread.
*/ */
public static void discoverServers(Consumer<Host> cons, Runnable done){ public void discoverServers(Consumer<Host> cons, Runnable done){
clientProvider.discover(cons, done); provider.discoverServers(cons, done);
} }
/** /**
* Returns a list of all connections IDs. * Returns a list of all connections IDs.
*/ */
public static Iterable<NetConnection> getConnections(){ public Iterable<NetConnection> getConnections(){
return (Iterable<NetConnection>)serverProvider.getConnections(); return (Iterable<NetConnection>)provider.getConnections();
} }
/** /**
* Returns a connection by ID * Returns a connection by ID
*/ */
public static NetConnection getConnection(int id){ public NetConnection getConnection(int id){
return serverProvider.getByID(id); return provider.getConnection(id);
} }
/** /**
* Send an object to all connected clients, or to the server if this is a client. * Send an object to all connected clients, or to the server if this is a client.
*/ */
public static void send(Object object, SendMode mode){ public void send(Object object, SendMode mode){
if(server){ if(server){
if(serverProvider != null) serverProvider.sendServer(object, mode); provider.sendServer(object, mode);
}else{ }else{
if(clientProvider != null) clientProvider.sendClient(object, mode); provider.sendClient(object, mode);
} }
} }
/** /**
* Send an object to a certain client. Server-side only * Send an object to a certain client. Server-side only
*/ */
public static void sendTo(int id, Object object, SendMode mode){ public void sendTo(int id, Object object, SendMode mode){
serverProvider.sendServerTo(id, object, mode); provider.sendServerTo(id, object, mode);
} }
/** /**
* Send an object to everyone EXCEPT certain client. Server-side only * Send an object to everyone EXCEPT certain client. Server-side only
*/ */
public static void sendExcept(int id, Object object, SendMode mode){ public void sendExcept(int id, Object object, SendMode mode){
serverProvider.sendServerExcept(id, object, mode); provider.sendServerExcept(id, object, mode);
} }
/** /**
* Send a stream to a specific client. Server-side only. * Send a stream to a specific client. Server-side only.
*/ */
public static void sendStream(int id, Streamable stream){ public void sendStream(int id, Streamable stream){
serverProvider.sendServerStream(id, stream); provider.sendServerStream(id, stream);
}
/**
* Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server.
*/
public static void setClientProvider(ClientProvider provider){
Net.clientProvider = provider;
}
/**
* Sets the net serverProvider, e.g. what handles hosting a server.
*/
public static void setServerProvider(ServerProvider provider){
Net.serverProvider = provider;
} }
/** /**
* Registers a client listener for when an object is recieved. * Registers a client listener for when an object is recieved.
*/ */
public static <T> void handleClient(Class<T> type, Consumer<T> listener){ public <T> void handleClient(Class<T> type, Consumer<T> listener){
clientListeners.put(type, listener); clientListeners.put(type, listener);
} }
/** /**
* Registers a server listener for when an object is recieved. * Registers a server listener for when an object is recieved.
*/ */
public static <T> void handleServer(Class<T> type, BiConsumer<Integer, T> listener){ public <T> void handleServer(Class<T> type, BiConsumer<NetConnection, T> listener){
serverListeners.put(type, (BiConsumer<Integer, Object>)listener); serverListeners.put(type, (BiConsumer<NetConnection, Object>)listener);
} }
/** /**
* Call to handle a packet being recieved for the client. * Call to handle a packet being recieved for the client.
*/ */
public static void handleClientReceived(Object object){ public void handleClientReceived(Object object){
if(object instanceof StreamBegin){ if(object instanceof StreamBegin){
StreamBegin b = (StreamBegin)object; StreamBegin b = (StreamBegin)object;
@@ -276,7 +267,7 @@ public class Net{
/** /**
* Call to handle a packet being recieved for the server. * Call to handle a packet being recieved for the server.
*/ */
public static void handleServerReceived(int connection, Object object){ public void handleServerReceived(NetConnection connection, Object object){
if(serverListeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null) if(serverListeners.get(object.getClass()) != null)
@@ -290,50 +281,33 @@ public class Net{
/** /**
* Pings a host in an new thread. If an error occured, failed() should be called with the exception. * Pings a host in an new thread. If an error occured, failed() should be called with the exception.
*/ */
public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){ public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
clientProvider.pingHost(address, port, valid, failed); provider.pingHost(address, port, valid, failed);
}
/**
* Update client ping.
*/
public static void updatePing(){
clientProvider.updatePing();
}
/**
* Get the client ping. Only valid after updatePing().
*/
public static int getPing(){
return server() ? 0 : clientProvider.getPing();
} }
/** /**
* Whether the net is active, e.g. whether this is a multiplayer game. * Whether the net is active, e.g. whether this is a multiplayer game.
*/ */
public static boolean active(){ public boolean active(){
return active; return active;
} }
/** /**
* Whether this is a server or not. * Whether this is a server or not.
*/ */
public static boolean server(){ public boolean server(){
return server && active; return server && active;
} }
/** /**
* Whether this is a client or not. * Whether this is a client or not.
*/ */
public static boolean client(){ public boolean client(){
return !server && active; return !server && active;
} }
public static void dispose(){ public void dispose(){
if(clientProvider != null) clientProvider.dispose(); provider.dispose();
if(serverProvider != null) serverProvider.close();
clientProvider = null;
serverProvider = null;
server = false; server = false;
active = false; active = false;
} }
@@ -343,46 +317,32 @@ public class Net{
} }
/** Client implementation. */ /** Client implementation. */
public interface ClientProvider{ public interface NetProvider{
/** Connect to a server. */ /** Connect to a server. */
void connect(String ip, int port, Runnable success) throws IOException; void connectClient(String ip, int port, Runnable success) throws IOException;
/** Send an object to the server. */ /** Send an object to the server. */
void sendClient(Object object, SendMode mode); void sendClient(Object object, SendMode mode);
/** Update the ping. Should be done every second or so. */
void updatePing();
/** Get ping in milliseconds. Will only be valid after a call to updatePing. */
int getPing();
/** Disconnect from the server. */ /** Disconnect from the server. */
void disconnect(); void disconnectClient();
/** /**
* Discover servers. This should run the callback regardless of whether any servers are found. Should not block. * Discover servers. This should run the callback regardless of whether any servers are found. Should not block.
* Callback should be run on libGDX main thread. * Callback should be run on libGDX main thread.
* @param done is the callback that should run after discovery. * @param done is the callback that should run after discovery.
*/ */
void discover(Consumer<Host> callback, Runnable done); void discoverServers(Consumer<Host> callback, Runnable done);
/** Ping a host. If an error occured, failed() should be called with the exception. */ /** Ping a host. If an error occured, failed() should be called with the exception. */
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed); void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
/** Close all connections. */
default void dispose(){
disconnect();
}
}
/** Server implementation. */
public interface ServerProvider{
/** Host a server at specified port. */ /** Host a server at specified port. */
void host(int port) throws IOException; void hostServer(int port) throws IOException;
/** Sends a large stream of data to a specific client. */ /** Sends a large stream of data to a specific client. */
default void sendServerStream(int id, Streamable stream){ default void sendServerStream(int id, Streamable stream){
NetConnection connection = getByID(id); NetConnection connection = getConnection(id);
if(connection == null) return; if(connection == null) return;
try{ try{
int cid; int cid;
@@ -413,7 +373,7 @@ public class Net{
} }
default void sendServerTo(int id, Object object, SendMode mode){ default void sendServerTo(int id, Object object, SendMode mode){
NetConnection conn = getByID(id); NetConnection conn = getConnection(id);
if(conn == null){ if(conn == null){
Log.err("Failed to find connection with ID {0}.", id); Log.err("Failed to find connection with ID {0}.", id);
return; return;
@@ -429,13 +389,26 @@ public class Net{
} }
} }
/** Close the server connection. */ /** Returns a connection by ID. */
void close(); default NetConnection getConnection(int id){
for(NetConnection con : getConnections()){
if(con.id == id){
return con;
}
}
return null;
}
/** Return all connected users. */ /** Return all connected users. */
Iterable<? extends NetConnection> getConnections(); Iterable<? extends NetConnection> getConnections();
/** Returns a connection by ID. */ /** Close the server connection. */
NetConnection getByID(int id); void closeServer();
/** Close all connections. */
default void dispose(){
disconnectClient();
closeServer();
}
} }
} }

View File

@@ -1,6 +1,14 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.annotations.Annotations.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.net.Administration.*;
import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*;
import static io.anuke.mindustry.Vars.netServer;
public abstract class NetConnection{ public abstract class NetConnection{
private static int lastID; private static int lastID;
@@ -10,6 +18,7 @@ public abstract class NetConnection{
public boolean modclient; public boolean modclient;
public boolean mobile; public boolean mobile;
public @Nullable Player player;
/** ID of last recieved client snapshot. */ /** ID of last recieved client snapshot. */
public int lastRecievedClientSnapshot = -1; public int lastRecievedClientSnapshot = -1;
@@ -26,6 +35,22 @@ public abstract class NetConnection{
this.address = address; this.address = address;
} }
public void kick(KickReason reason){
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", this.id, address, reason.name());
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
PlayerInfo info = netServer.admins.getInfo(player.uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
}
Call.onKick(id, reason);
Time.runTask(2f, this::close);
netServer.admins.save();
}
public boolean isConnected(){ public boolean isConnected(){
return true; return true;
} }

View File

@@ -14,8 +14,8 @@ import io.anuke.mindustry.entities.bullet.*;
import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.gen.Sounds;
import io.anuke.mindustry.net.Net; import static io.anuke.mindustry.Vars.net;
public class Weapon{ public class Weapon{
public final String name; public final String name;
@@ -73,7 +73,7 @@ public class Weapon{
if(player == null) return; if(player == null) return;
//clients do not see their own shoot events: they are simulated completely clientside to prevent laggy visuals //clients do not see their own shoot events: they are simulated completely clientside to prevent laggy visuals
//messing with the firerate or any other stats does not affect the server (take that, script kiddies!) //messing with the firerate or any other stats does not affect the server (take that, script kiddies!)
if(Net.client() && player == Vars.player){ if(net.client() && player == Vars.player){
return; return;
} }
@@ -154,7 +154,7 @@ public class Weapon{
} }
public void shoot(ShooterTrait p, float x, float y, float angle, boolean left){ public void shoot(ShooterTrait p, float x, float y, float angle, boolean left){
if(Net.client()){ if(net.client()){
//call it directly, don't invoke on server //call it directly, don't invoke on server
shootDirect(p, x, y, angle, left); shootDirect(p, x, y, angle, left);
}else{ }else{

View File

@@ -19,7 +19,6 @@ import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.game.Saves.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.io.SaveIO.*; import io.anuke.mindustry.io.SaveIO.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.type.Zone.*; import io.anuke.mindustry.type.Zone.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
@@ -105,7 +104,7 @@ public class DeployDialog extends FloatingDialog{
hide(); hide();
ui.loadAnd(() -> { ui.loadAnd(() -> {
logic.reset(); logic.reset();
Net.reset(); net.reset();
try{ try{
control.saves.getZoneSlot().load(); control.saves.getZoneSlot().load();
state.set(State.playing); state.set(State.playing);

View File

@@ -26,7 +26,7 @@ public class FloatingDialog extends Dialog{
hidden(() -> { hidden(() -> {
if(shouldPause && !state.is(State.menu)){ if(shouldPause && !state.is(State.menu)){
if(!wasPaused || Net.active()){ if(!wasPaused || net.active()){
state.set(State.playing); state.set(State.playing);
} }
} }

View File

@@ -1,14 +1,12 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core; import io.anuke.arc.*;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.*;
import io.anuke.arc.scene.ui.ImageButton; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.util.Strings; import io.anuke.arc.util.*;
import io.anuke.arc.util.Time; import io.anuke.mindustry.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.net.Net;
import java.io.IOException; import java.io.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -65,7 +63,7 @@ public class HostDialog extends FloatingDialog{
ui.loadfrag.show("$hosting"); ui.loadfrag.show("$hosting");
Time.runTask(5f, () -> { Time.runTask(5f, () -> {
try{ try{
Net.host(Vars.port); net.host(Vars.port);
player.isAdmin = true; player.isAdmin = true;
}catch(IOException e){ }catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true))); ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));

View File

@@ -12,7 +12,6 @@ import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.mindustry.*; import io.anuke.mindustry.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
@@ -68,7 +67,7 @@ public class JoinDialog extends FloatingDialog{
refreshRemote(); refreshRemote();
} }
add.hide(); add.hide();
}).disabled(b -> Core.settings.getString("ip").isEmpty() || Net.active()); }).disabled(b -> Core.settings.getString("ip").isEmpty() || net.active());
add.shown(() -> { add.shown(() -> {
add.title.setText(renaming != null ? "$server.edit" : "$server.add"); add.title.setText(renaming != null ? "$server.edit" : "$server.add");
@@ -173,7 +172,7 @@ public class JoinDialog extends FloatingDialog{
server.content.clear(); server.content.clear();
server.content.label(() -> Core.bundle.get("server.refreshing") + Strings.animated(Time.time(), 4, 11, ".")); server.content.label(() -> Core.bundle.get("server.refreshing") + Strings.animated(Time.time(), 4, 11, "."));
Net.pingHost(server.ip, server.port, host -> setupServer(server, host), e -> { net.pingHost(server.ip, server.port, host -> setupServer(server, host), e -> {
server.content.clear(); server.content.clear();
server.content.add("$host.invalid"); server.content.add("$host.invalid");
}); });
@@ -276,9 +275,9 @@ public class JoinDialog extends FloatingDialog{
local.clear(); local.clear();
local.background((Drawable)null); local.background((Drawable)null);
local.table("button", t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX(); local.table("button", t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
Net.discoverServers(this::addLocalHost, this::finishLocalHosts); net.discoverServers(this::addLocalHost, this::finishLocalHosts);
for(String host : defaultServers){ for(String host : defaultServers){
Net.pingHost(host, port, this::addLocalHost, e -> {}); net.pingHost(host, port, this::addLocalHost, e -> {});
} }
} }
@@ -325,9 +324,9 @@ public class JoinDialog extends FloatingDialog{
Time.runTask(2f, () -> { Time.runTask(2f, () -> {
logic.reset(); logic.reset();
Net.reset(); net.reset();
Vars.netClient.beginConnecting(); Vars.netClient.beginConnecting();
Net.connect(ip, port, () -> { net.connect(ip, port, () -> {
hide(); hide();
add.hide(); add.hide();
}); });

View File

@@ -13,7 +13,6 @@ import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.game.Saves.*;
import io.anuke.mindustry.io.*; import io.anuke.mindustry.io.*;
import io.anuke.mindustry.io.SaveIO.*; import io.anuke.mindustry.io.SaveIO.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
import java.io.*; import java.io.*;
@@ -193,7 +192,7 @@ public class LoadDialog extends FloatingDialog{
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
Net.reset(); net.reset();
slot.load(); slot.load();
state.set(State.playing); state.set(State.playing);
}catch(SaveException e){ }catch(SaveException e){

View File

@@ -52,7 +52,7 @@ public class PausedDialog extends FloatingDialog{
if(!world.isZone() && !state.isEditor()){ if(!world.isZone() && !state.isEditor()){
cont.row(); cont.row();
cont.addButton("$savegame", save::show); cont.addButton("$savegame", save::show);
cont.addButton("$loadgame", load::show).disabled(b -> Net.active()); cont.addButton("$loadgame", load::show).disabled(b -> net.active());
} }
cont.row(); cont.row();
@@ -63,7 +63,7 @@ public class PausedDialog extends FloatingDialog{
}else{ }else{
ui.host.show(); ui.host.show();
} }
}).disabled(b -> Net.active()).colspan(2).width(dw * 2 + 20f); }).disabled(b -> net.active()).colspan(2).width(dw * 2 + 20f);
} }
cont.row(); cont.row();
@@ -82,12 +82,12 @@ public class PausedDialog extends FloatingDialog{
cont.row(); cont.row();
cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> Net.active()); cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> net.active());
}else{ }else{
cont.row(); cont.row();
} }
cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> Net.active()); cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> net.active());
cont.addRowImageTextButton("$quit", "icon-quit", isize, this::showQuitConfirm); cont.addRowImageTextButton("$quit", "icon-quit", isize, this::showQuitConfirm);
} }
@@ -99,8 +99,8 @@ public class PausedDialog extends FloatingDialog{
Core.settings.put("playedtutorial", true); Core.settings.put("playedtutorial", true);
Core.settings.save(); Core.settings.save();
} }
wasClient = Net.client(); wasClient = net.client();
if(Net.client()) netClient.disconnectQuietly(); if(net.client()) netClient.disconnectQuietly();
runExitSave(); runExitSave();
hide(); hide();
}); });

View File

@@ -36,7 +36,7 @@ public class SettingsMenuDialog extends SettingsDialog{
hidden(() -> { hidden(() -> {
Sounds.back.play(); Sounds.back.play();
if(!state.is(State.menu)){ if(!state.is(State.menu)){
if(!wasPaused || Net.active()) if(!wasPaused || net.active())
state.set(State.playing); state.set(State.playing);
} }
}); });

View File

@@ -1,28 +1,23 @@
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.Input.TextInput; import io.anuke.arc.Input.*;
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.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.*;
import io.anuke.arc.scene.Group; import io.anuke.arc.scene.*;
import io.anuke.arc.scene.ui.Label; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.Label.LabelStyle; import io.anuke.arc.scene.ui.Label.*;
import io.anuke.arc.scene.ui.TextField; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.util.*;
import io.anuke.arc.scene.ui.layout.UnitScl; import io.anuke.mindustry.*;
import io.anuke.arc.util.Align; import io.anuke.mindustry.gen.*;
import io.anuke.arc.util.Time; import io.anuke.mindustry.input.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.net.Net;
import static io.anuke.arc.Core.input; import static io.anuke.arc.Core.*;
import static io.anuke.arc.Core.scene; import static io.anuke.mindustry.Vars.net;
import static io.anuke.mindustry.Vars.maxTextLength; import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.mobile;
public class ChatFragment extends Table{ public class ChatFragment extends Table{
private final static int messagesShown = 10; private final static int messagesShown = 10;
@@ -53,7 +48,7 @@ public class ChatFragment extends Table{
font = scene.skin.getFont("default"); font = scene.skin.getFont("default");
visible(() -> { visible(() -> {
if(!Net.active() && messages.size > 0){ if(!net.active() && messages.size > 0){
clearMessages(); clearMessages();
if(chatOpen){ if(chatOpen){
@@ -61,12 +56,12 @@ public class ChatFragment extends Table{
} }
} }
return Net.active(); return net.active();
}); });
update(() -> { update(() -> {
if(Net.active() && input.keyTap(Binding.chat)){ if(net.active() && input.keyTap(Binding.chat)){
toggle(); toggle();
} }

View File

@@ -23,7 +23,6 @@ import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.input.*; import io.anuke.mindustry.input.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
@@ -66,13 +65,13 @@ public class HudFragment extends Fragment{
flip = select.addImageButton("icon-arrow-up", style, iconsize, this::toggleMenus).get(); flip = select.addImageButton("icon-arrow-up", style, iconsize, this::toggleMenus).get();
select.addImageButton("icon-pause", style, iconsize, () -> { select.addImageButton("icon-pause", style, iconsize, () -> {
if(Net.active()){ if(net.active()){
ui.listfrag.toggle(); ui.listfrag.toggle();
}else{ }else{
state.set(state.is(State.paused) ? State.playing : State.paused); state.set(state.is(State.paused) ? State.playing : State.paused);
} }
}).name("pause").update(i -> { }).name("pause").update(i -> {
if(Net.active()){ if(net.active()){
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-players"); i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-players");
}else{ }else{
i.setDisabled(false); i.setDisabled(false);
@@ -81,7 +80,7 @@ public class HudFragment extends Fragment{
}).get(); }).get();
select.addImageButton("icon-settings", style, iconsize, () -> { select.addImageButton("icon-settings", style, iconsize, () -> {
if(Net.active() && mobile){ if(net.active() && mobile){
if(ui.chatfrag.chatOpen()){ if(ui.chatfrag.chatOpen()){
ui.chatfrag.hide(); ui.chatfrag.hide();
}else{ }else{
@@ -93,7 +92,7 @@ public class HudFragment extends Fragment{
ui.database.show(); ui.database.show();
} }
}).update(i -> { }).update(i -> {
if(Net.active() && mobile){ if(net.active() && mobile){
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-chat"); i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-chat");
}else{ }else{
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-database"); i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-database");
@@ -244,7 +243,7 @@ public class HudFragment extends Fragment{
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style("outline"); info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style("outline");
info.row(); info.row();
info.label(() -> ping.get(Net.getPing())).visible(Net::client).left().style("outline"); info.label(() -> ping.get(netClient.getPing())).visible(net::client).left().style("outline");
}).top().left(); }).top().left();
}); });
@@ -542,7 +541,7 @@ public class HudFragment extends Fragment{
private boolean inLaunchWave(){ private boolean inLaunchWave(){
return world.isZone() && return world.isZone() &&
world.getZone().metCondition() && world.getZone().metCondition() &&
!Net.client() && !net.client() &&
state.wave % world.getZone().launchPeriod == 0 && !spawner.isSpawning(); state.wave % world.getZone().launchPeriod == 0 && !spawner.isSpawning();
} }
@@ -639,12 +638,12 @@ public class HudFragment extends Fragment{
} }
private boolean canSkipWave(){ private boolean canSkipWave(){
return state.rules.waves && ((Net.server() || player.isAdmin) || !Net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial; return state.rules.waves && ((net.server() || player.isAdmin) || !net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial;
} }
private void addPlayButton(Table table){ private void addPlayButton(Table table){
table.right().addImageButton("icon-play", "right", 30f, () -> { table.right().addImageButton("icon-play", "right", 30f, () -> {
if(Net.client() && player.isAdmin){ if(net.client() && player.isAdmin){
Call.onAdminRequest(player, AdminAction.wave); Call.onAdminRequest(player, AdminAction.wave);
}else if(inLaunchWave()){ }else if(inLaunchWave()){
ui.showConfirm("$confirm", "$launch.skip.confirm", () -> !canSkipWave(), () -> state.wavetime = 0f); ui.showConfirm("$confirm", "$launch.skip.confirm", () -> !canSkipWave(), () -> state.wavetime = 0f);

View File

@@ -29,7 +29,7 @@ public class PlayerListFragment extends Fragment{
parent.fill(cont -> { parent.fill(cont -> {
cont.visible(() -> visible); cont.visible(() -> visible);
cont.update(() -> { cont.update(() -> {
if(!(Net.active() && !state.is(State.menu))){ if(!(net.active() && !state.is(State.menu))){
visible = false; visible = false;
return; return;
} }
@@ -52,8 +52,8 @@ public class PlayerListFragment extends Fragment{
pane.table(menu -> { pane.table(menu -> {
menu.defaults().growX().height(50f).fillY(); menu.defaults().growX().height(50f).fillY();
menu.addButton("$server.bans", ui.bans::show).disabled(b -> Net.client()); menu.addButton("$server.bans", ui.bans::show).disabled(b -> net.client());
menu.addButton("$server.admins", ui.admins::show).disabled(b -> Net.client()); menu.addButton("$server.admins", ui.admins::show).disabled(b -> net.client());
menu.addButton("$close", this::toggle); menu.addButton("$close", this::toggle);
}).margin(0f).pad(10f).growX(); }).margin(0f).pad(10f).growX();
@@ -72,7 +72,7 @@ public class PlayerListFragment extends Fragment{
playerGroup.all().each(user -> { playerGroup.all().each(user -> {
NetConnection connection = user.con; NetConnection connection = user.con;
if(connection == null && Net.server() && !user.isLocal) return; if(connection == null && net.server() && !user.isLocal) return;
Table button = new Table(); Table button = new Table();
button.left(); button.left();
@@ -96,9 +96,9 @@ public class PlayerListFragment extends Fragment{
button.labelWrap("[#" + user.color.toString().toUpperCase() + "]" + user.name).width(170f).pad(10); button.labelWrap("[#" + user.color.toString().toUpperCase() + "]" + user.name).width(170f).pad(10);
button.add().grow(); button.add().grow();
button.addImage("icon-admin").size(iconsize).visible(() -> user.isAdmin && !(!user.isLocal && Net.server())).padRight(5).get().updateVisibility(); button.addImage("icon-admin").size(iconsize).visible(() -> user.isAdmin && !(!user.isLocal && net.server())).padRight(5).get().updateVisibility();
if((Net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || Net.server())){ if((net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || net.server())){
button.add().growY(); button.add().growY();
float bs = (h) / 2f; float bs = (h) / 2f;
@@ -114,7 +114,7 @@ public class PlayerListFragment extends Fragment{
t.row(); t.row();
t.addImageButton("icon-admin-small", "clear-toggle-partial", iconsizesmall, () -> { t.addImageButton("icon-admin-small", "clear-toggle-partial", iconsizesmall, () -> {
if(Net.client()) return; if(net.client()) return;
String id = user.uuid; String id = user.uuid;
@@ -125,8 +125,8 @@ public class PlayerListFragment extends Fragment{
} }
}) })
.update(b -> b.setChecked(user.isAdmin)) .update(b -> b.setChecked(user.isAdmin))
.disabled(b -> Net.client()) .disabled(b -> net.client())
.touchable(() -> Net.client() ? Touchable.disabled : Touchable.enabled) .touchable(() -> net.client() ? Touchable.disabled : Touchable.enabled)
.checked(user.isAdmin); .checked(user.isAdmin);
t.addImageButton("icon-zoom-small", "clear-partial", iconsizesmall, () -> Call.onAdminRequest(user, AdminAction.trace)); t.addImageButton("icon-zoom-small", "clear-partial", iconsizesmall, () -> Call.onAdminRequest(user, AdminAction.trace));

View File

@@ -24,7 +24,6 @@ import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.input.InputHandler.*; import io.anuke.mindustry.input.InputHandler.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.blocks.*;
@@ -300,7 +299,7 @@ public class Block extends BlockStorage{
/** Called after the block is placed by anyone. */ /** Called after the block is placed by anyone. */
@CallSuper @CallSuper
public void placed(Tile tile){ public void placed(Tile tile){
if(Net.client()) return; if(net.client()) return;
if((consumesPower && !outputsPower) || (!consumesPower && outputsPower)){ if((consumesPower && !outputsPower) || (!consumesPower && outputsPower)){
int range = 10; int range = 10;

View File

@@ -4,10 +4,11 @@ import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
import static io.anuke.mindustry.Vars.net;
public class RespawnBlock{ public class RespawnBlock{
public static void drawRespawn(Tile tile, float heat, float progress, float time, Player player, Mech to){ public static void drawRespawn(Tile tile, float heat, float progress, float time, Player player, Mech to){
@@ -64,7 +65,7 @@ public class RespawnBlock{
} }
Draw.reset(); Draw.reset();
if(Net.active() && player != null){ if(net.active() && player != null){
tile.block().drawPlaceText(player.name, tile.x, tile.y - (Math.max((tile.block().size-1)/2, 0)), true); tile.block().drawPlaceText(player.name, tile.x, tile.y - (Math.max((tile.block().size-1)/2, 0)), true);
} }
} }

View File

@@ -100,7 +100,7 @@ public class PowerNode extends PowerBlock{
@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) || other.block() instanceof PowerNode) && 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;

View File

@@ -94,7 +94,7 @@ public class CoreBlock extends StorageBlock{
@Override @Override
public void handleItem(Item item, Tile tile, Tile source){ public void handleItem(Item item, Tile tile, Tile source){
if(Net.server() || !Net.active()){ if(net.server() || !net.active()){
super.handleItem(item, tile, source); super.handleItem(item, tile, source);
if(state.rules.tutorial){ if(state.rules.tutorial){
Events.fire(new CoreItemDeliverEvent()); Events.fire(new CoreItemDeliverEvent());

View File

@@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.Bar; import io.anuke.mindustry.ui.Bar;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
@@ -23,6 +22,7 @@ import io.anuke.mindustry.world.consumers.ConsumeType;
import io.anuke.mindustry.world.meta.*; import io.anuke.mindustry.world.meta.*;
import java.io.*; import java.io.*;
import static io.anuke.mindustry.Vars.*;
public class UnitFactory extends Block{ public class UnitFactory extends Block{
protected UnitType type; protected UnitType type;
@@ -54,7 +54,7 @@ public class UnitFactory extends Block{
Effects.shake(2f, 3f, entity); Effects.shake(2f, 3f, entity);
Effects.effect(Fx.producesmoke, tile.drawx(), tile.drawy()); Effects.effect(Fx.producesmoke, tile.drawx(), tile.drawy());
if(!Net.client()){ if(!net.client()){
BaseUnit unit = factory.type.create(tile.getTeam()); BaseUnit unit = factory.type.create(tile.getTeam());
unit.setSpawner(tile); unit.setSpawner(tile);
unit.set(tile.drawx() + Mathf.range(4), tile.drawy() + Mathf.range(4)); unit.set(tile.drawx() + Mathf.range(4), tile.drawy() + Mathf.range(4));

View File

@@ -18,8 +18,8 @@ import io.anuke.mindustry.*;
import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.desktop.steam.*; import io.anuke.mindustry.desktop.steam.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
@@ -130,12 +130,7 @@ public class DesktopLauncher extends ClientLauncher{
} }
if(steam){ if(steam){
SteamCoreNetImpl net = steamCore = new SteamCoreNetImpl(); SteamCoreNetImpl net = steamCore = new SteamCoreNetImpl(new ArcNetImpl());
Net.setClientProvider(net);
Net.setServerProvider(net);
}else{
Net.setClientProvider(new ArcNetClient());
Net.setServerProvider(new ArcNetServer());
} }
} }
@@ -162,6 +157,11 @@ public class DesktopLauncher extends ClientLauncher{
}); });
} }
@Override
public NetProvider getNet(){
return steam ? steamCore : new ArcNetImpl();
}
@Override @Override
public void updateLobby(){ public void updateLobby(){
steamCore.updateLobby(); steamCore.updateLobby();
@@ -176,7 +176,7 @@ public class DesktopLauncher extends ClientLauncher{
if(!state.is(State.menu)){ if(!state.is(State.menu)){
String map = world.getMap() == null ? "Unknown Map" : world.isZone() ? world.getZone().localizedName : Strings.capitalize(world.getMap().name()); String map = world.getMap() == null ? "Unknown Map" : world.isZone() ? world.getZone().localizedName : Strings.capitalize(world.getMap().name());
String mode = state.rules.pvp ? "PvP" : state.rules.attackMode ? "Attack" : "Survival"; String mode = state.rules.pvp ? "PvP" : state.rules.attackMode ? "Attack" : "Survival";
String players = Net.active() && playerGroup.size() > 1 ? " | " + playerGroup.size() + " Players" : ""; String players = net.active() && playerGroup.size() > 1 ? " | " + playerGroup.size() + " Players" : "";
presence.state = mode + players; presence.state = mode + players;

View File

@@ -9,10 +9,9 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.*; import io.anuke.arc.util.pooling.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
@@ -23,11 +22,13 @@ import java.util.concurrent.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmakingCallback, SteamFriendsCallback, ClientProvider, ServerProvider{ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmakingCallback, SteamFriendsCallback, NetProvider{
public final SteamNetworking snet = new SteamNetworking(this); public final SteamNetworking snet = new SteamNetworking(this);
public final SteamMatchmaking smat = new SteamMatchmaking(this); public final SteamMatchmaking smat = new SteamMatchmaking(this);
public final SteamFriends friends = new SteamFriends(this); public final SteamFriends friends = new SteamFriends(this);
final NetProvider provider;
final PacketSerializer serializer = new PacketSerializer(); final PacketSerializer serializer = new PacketSerializer();
final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024 * 4); final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024 * 4);
final ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024 * 4); final ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024 * 4);
@@ -40,7 +41,9 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
Consumer<Host> lobbyCallback; Consumer<Host> lobbyCallback;
Runnable lobbyDoneCallback, joinCallback; Runnable lobbyDoneCallback, joinCallback;
public SteamCoreNetImpl(){ public SteamCoreNetImpl(NetProvider provider){
this.provider = provider;
Events.on(ClientLoadEvent.class, e -> Core.app.addListener(new ApplicationListener(){ Events.on(ClientLoadEvent.class, e -> Core.app.addListener(new ApplicationListener(){
//read packets //read packets
int length; int length;
@@ -55,15 +58,15 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
int fromID = from.getAccountID(); int fromID = from.getAccountID();
Object output = serializer.read(readBuffer); Object output = serializer.read(readBuffer);
if(Net.server()){ if(net.server()){
SteamConnection con = steamConnections.get(fromID); SteamConnection con = steamConnections.get(fromID);
if(con != null){ if(con != null){
Net.handleServerReceived(con.id, output); net.handleServerReceived(con, output);
}else{ }else{
Log.err("Unknown user with ID: {0}", fromID); Log.err("Unknown user with ID: {0}", fromID);
} }
}else if(currentServer != null && fromID == currentServer.getAccountID()){ }else if(currentServer != null && fromID == currentServer.getAccountID()){
Net.handleClientReceived(output); net.handleClientReceived(output);
} }
}catch(SteamException e){ }catch(SteamException e){
e.printStackTrace(); e.printStackTrace();
@@ -73,68 +76,71 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
})); }));
Events.on(WaveEvent.class, e -> { Events.on(WaveEvent.class, e -> {
if(currentLobby != null && Net.server()){ if(currentLobby != null && net.server()){
smat.setLobbyData(currentLobby, "wave", state.wave + ""); smat.setLobbyData(currentLobby, "wave", state.wave + "");
} }
}); });
} }
public boolean isSteamClient(){
return currentServer != null;
}
@Override @Override
public void connect(String ip, int port, Runnable success) throws IOException{ public void connectClient(String ip, int port, Runnable success) throws IOException{
if(ip.startsWith("steam:")){ if(ip.startsWith("steam:")){
String lobbyname = ip.substring("steam:".length()); String lobbyname = ip.substring("steam:".length());
SteamID lobby = lobbyIDs.get(lobbyname); SteamID lobby = lobbyIDs.get(lobbyname);
if(lobby == null) throw new IOException("Lobby not found."); if(lobby == null) throw new IOException("Lobby not found.");
joinCallback = success; joinCallback = success;
smat.joinLobby(lobby); smat.joinLobby(lobby);
}else{
provider.connectClient(ip, port, success);
} }
//else, no
} }
@Override @Override
public void sendClient(Object object, SendMode mode){ public void sendClient(Object object, SendMode mode){
if(currentServer == null){ if(isSteamClient()){
Log.info("Not connected, quitting."); if(currentServer == null){
return; Log.info("Not connected, quitting.");
return;
}
try{
writeBuffer.limit(writeBuffer.capacity());
writeBuffer.position(0);
serializer.write(writeBuffer, object);
writeBuffer.flip();
snet.sendP2PPacket(currentServer, writeBuffer, mode == SendMode.tcp ? P2PSend.Reliable : P2PSend.UnreliableNoDelay, 0);
}catch(Exception e){
net.showError(e);
}
Pools.free(object);
}else{
provider.sendClient(object, mode);
} }
try{
writeBuffer.limit(writeBuffer.capacity());
writeBuffer.position(0);
serializer.write(writeBuffer, object);
writeBuffer.flip();
snet.sendP2PPacket(currentServer, writeBuffer, mode == SendMode.tcp ? P2PSend.Reliable : P2PSend.UnreliableNoDelay, 0);
}catch(Exception e){
Net.showError(e);
}
Pools.free(object);
} }
@Override
public void updatePing(){
//no
}
@Override @Override
public int getPing(){ public void disconnectClient(){
//absolutely not if(isSteamClient()){
return 0; if(currentLobby != null){
} smat.leaveLobby(currentLobby);
snet.closeP2PSessionWithUser(currentServer);
@Override currentServer = null;
public void disconnect(){ currentLobby = null;
if(currentLobby != null){ net.handleClientReceived(new Disconnect());
smat.leaveLobby(currentLobby); }
snet.closeP2PSessionWithUser(currentServer); }else{
currentServer = null; provider.disconnectClient();
currentLobby = null;
Net.handleClientReceived(new Disconnect());
} }
} }
@Override @Override
public void discover(Consumer<Host> callback, Runnable done){ public void discoverServers(Consumer<Host> callback, Runnable done){
smat.addRequestLobbyListResultCountFilter(32); smat.addRequestLobbyListResultCountFilter(32);
smat.requestLobbyList(); smat.requestLobbyList();
lobbyCallback = callback; lobbyCallback = callback;
@@ -147,18 +153,18 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
} }
@Override @Override
public void host(int port) throws IOException{ public void hostServer(int port) throws IOException{
smat.createLobby(Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly, 32); smat.createLobby(Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly, 32);
} }
public void updateLobby(){ public void updateLobby(){
if(currentLobby != null && Net.server()){ if(currentLobby != null && net.server()){
smat.setLobbyType(currentLobby, Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly); smat.setLobbyType(currentLobby, Core.settings.getBool("publichost") ? LobbyType.Public : LobbyType.FriendsOnly);
} }
} }
@Override @Override
public void close(){ public void closeServer(){
if(currentLobby != null){ if(currentLobby != null){
smat.leaveLobby(currentLobby); smat.leaveLobby(currentLobby);
for(SteamConnection con : steamConnections.values()){ for(SteamConnection con : steamConnections.values()){
@@ -175,18 +181,6 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
return connections; return connections;
} }
@Override
public NetConnection getByID(int id){
for(int i = 0; i < connections.size(); i++){
SteamConnection con = connections.get(i);
if(con.id == id){
return con;
}
}
return null;
}
void disconnectSteamUser(SteamID steamid){ void disconnectSteamUser(SteamID steamid){
//a client left //a client left
int sid = steamid.getAccountID(); int sid = steamid.getAccountID();
@@ -194,7 +188,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
if(steamConnections.containsKey(sid)){ if(steamConnections.containsKey(sid)){
SteamConnection con = steamConnections.get(sid); SteamConnection con = steamConnections.get(sid);
Net.handleServerReceived(con.id, new Disconnect()); net.handleServerReceived(con, new Disconnect());
steamConnections.remove(sid); steamConnections.remove(sid);
connections.remove(con); connections.remove(con);
} }
@@ -210,7 +204,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
Log.info("lobby invite {0} {1} {2}", steamIDLobby.getAccountID(), steamIDUser.getAccountID(), gameID); Log.info("lobby invite {0} {1} {2}", steamIDLobby.getAccountID(), steamIDUser.getAccountID(), gameID);
//ignore invites when hosting. //ignore invites when hosting.
if(Net.server()) return; if(net.server()) return;
ui.showConfirm("Someone has invited you to a game.", "But do you accept?", () -> { ui.showConfirm("Someone has invited you to a game.", "But do you accept?", () -> {
smat.joinLobby(steamIDLobby); smat.joinLobby(steamIDLobby);
@@ -231,8 +225,8 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
Connect con = new Connect(); Connect con = new Connect();
con.addressTCP = "steam:" + currentServer.getAccountID(); con.addressTCP = "steam:" + currentServer.getAccountID();
Net.setClientConnected(); net.setClientConnected();
Net.handleClientReceived(con); net.handleClientReceived(con);
} }
@Override @Override
@@ -244,11 +238,11 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
public void onLobbyChatUpdate(SteamID lobby, SteamID who, SteamID changer, ChatMemberStateChange change){ public void onLobbyChatUpdate(SteamID lobby, SteamID who, SteamID changer, ChatMemberStateChange change){
Log.info("lobby {0}: {1} caused {2}'s change: {3}", lobby.getAccountID(), who.getAccountID(), changer.getAccountID(), change); Log.info("lobby {0}: {1} caused {2}'s change: {3}", lobby.getAccountID(), who.getAccountID(), changer.getAccountID(), change);
if(change == ChatMemberStateChange.Disconnected || change == ChatMemberStateChange.Left){ if(change == ChatMemberStateChange.Disconnected || change == ChatMemberStateChange.Left){
if(Net.client()){ if(net.client()){
Log.info("Current host left."); Log.info("Current host left.");
//host left, leave as well //host left, leave as well
if(who == currentServer){ if(who == currentServer){
disconnect(); net.disconnect();
} }
}else{ }else{
//a client left //a client left
@@ -306,7 +300,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
@Override @Override
public void onLobbyCreated(SteamResult result, SteamID steamID){ public void onLobbyCreated(SteamResult result, SteamID steamID){
if(!Net.server()){ if(!net.server()){
Log.info("Lobby created on server: {0}, ignoring.", steamID); Log.info("Lobby created on server: {0}, ignoring.", steamID);
return; return;
} }
@@ -334,24 +328,24 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
@Override @Override
public void onP2PSessionConnectFail(SteamID steamIDRemote, P2PSessionError sessionError){ public void onP2PSessionConnectFail(SteamID steamIDRemote, P2PSessionError sessionError){
if(Net.server()){ if(net.server()){
Log.info("{0} has disconnected: {1}", steamIDRemote.getAccountID(), sessionError); Log.info("{0} has disconnected: {1}", steamIDRemote.getAccountID(), sessionError);
disconnectSteamUser(steamIDRemote); disconnectSteamUser(steamIDRemote);
}else if(steamIDRemote == currentServer){ }else if(steamIDRemote == currentServer){
Log.info("Disconnected! {1}: {0}", steamIDRemote.getAccountID(), sessionError); Log.info("Disconnected! {1}: {0}", steamIDRemote.getAccountID(), sessionError);
Net.handleClientReceived(new Disconnect()); net.handleClientReceived(new Disconnect());
} }
} }
@Override @Override
public void onP2PSessionRequest(SteamID steamIDRemote){ public void onP2PSessionRequest(SteamID steamIDRemote){
Log.info("Connection request: {0}", steamIDRemote.getAccountID()); Log.info("Connection request: {0}", steamIDRemote.getAccountID());
if(currentServer != null && !Net.server()){ if(currentServer != null && !net.server()){
Log.info("Am client"); Log.info("Am client");
if(steamIDRemote == currentServer){ if(steamIDRemote == currentServer){
snet.acceptP2PSessionWithUser(steamIDRemote); snet.acceptP2PSessionWithUser(steamIDRemote);
} }
}else if(Net.server()){ }else if(net.server()){
Log.info("Am server, accepting request."); Log.info("Am server, accepting request.");
//accept users on request //accept users on request
if(!steamConnections.containsKey(steamIDRemote.getAccountID())){ if(!steamConnections.containsKey(steamIDRemote.getAccountID())){
@@ -364,7 +358,7 @@ public class SteamCoreNetImpl implements SteamNetworkingCallback, SteamMatchmaki
steamConnections.put(steamIDRemote.getAccountID(), con); steamConnections.put(steamIDRemote.getAccountID(), con);
connections.add(con); connections.add(con);
Net.handleServerReceived(c.id, c); net.handleServerReceived(con, c);
} }
snet.acceptP2PSessionWithUser(steamIDRemote); snet.acceptP2PSessionWithUser(steamIDRemote);

View File

@@ -10,8 +10,8 @@ import io.anuke.arc.util.io.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Saves.*; import io.anuke.mindustry.game.Saves.*;
import io.anuke.mindustry.io.*; import io.anuke.mindustry.io.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*;
import org.robovm.apple.foundation.*; import org.robovm.apple.foundation.*;
import org.robovm.apple.uikit.*; import org.robovm.apple.uikit.*;
import org.robovm.objc.block.*; import org.robovm.objc.block.*;
@@ -28,8 +28,6 @@ public class IOSLauncher extends IOSApplication.Delegate{
@Override @Override
protected IOSApplication createApplication(){ protected IOSApplication createApplication(){
Net.setClientProvider(new ArcNetClient());
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;
@@ -40,6 +38,11 @@ public class IOSLauncher extends IOSApplication.Delegate{
IOSApplicationConfiguration config = new IOSApplicationConfiguration(); IOSApplicationConfiguration config = new IOSApplicationConfiguration();
return new IOSApplication(new ClientLauncher(){ return new IOSApplication(new ClientLauncher(){
@Override
public NetProvider getNet(){
return new ArcNetImpl();
}
@Override @Override
public void showFileChooser(boolean open, String extension, Consumer<FileHandle> cons){ public void showFileChooser(boolean open, String extension, Consumer<FileHandle> cons){
UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController(NSArray.fromStrings("public.archive")); UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController(NSArray.fromStrings("public.archive"));

View File

@@ -1,192 +0,0 @@
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, DcReason reason){
if(connection.getLastProtocolError() != null){
netClient.setQuiet();
}
Disconnect c = new Disconnect();
c.reason = reason.toString();
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 sendClient(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 if(e instanceof ClosedChannelException){
Core.app.post(() -> Net.showError(new IOException("alreadyconnected")));
}else{
Core.app.post(() -> Net.showError(e));
}
}
}

View File

@@ -0,0 +1,351 @@
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.*;
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 java.util.concurrent.*;
import static io.anuke.mindustry.Vars.*;
public class ArcNetImpl implements NetProvider{
final Client client;
final Supplier<DatagramPacket> packetSupplier = () -> new DatagramPacket(new byte[256], 256);
final Server server;
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
Thread serverThread;
public ArcNetImpl(){
client = new Client(8192, 4096, new PacketSerializer());
client.setDiscoveryPacket(packetSupplier);
client.addListener(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, DcReason reason){
if(connection.getLastProtocolError() != null){
netClient.setQuiet();
}
Disconnect c = new Disconnect();
c.reason = reason.toString();
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);
}
});
}
});
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);
});
server.addListener(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, c));
}
@Override
public void disconnected(Connection connection, DcReason reason){
ArcConnection k = getByArcID(connection.getID());
if(k == null) return;
Disconnect c = new Disconnect();
c.id = k.id;
c.reason = reason.toString();
Core.app.post(() -> {
net.handleServerReceived(k, c);
connections.remove(k);
});
}
@Override
public void received(Connection connection, Object object){
ArcConnection k = getByArcID(connection.getID());
if(object instanceof FrameworkMessage || k == null) return;
Core.app.post(() -> {
try{
net.handleServerReceived(k, 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();
}
});
}
});
}
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 connectClient(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 disconnectClient(){
client.close();
}
@Override
public void sendClient(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 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 discoverServers(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);
}
}
@Override
public Iterable<ArcConnection> getConnections(){
return connections;
}
@Override
public void sendServerStream(int id, Streamable stream){
ArcConnection connection = (ArcConnection)getConnection(id);
if(connection == null) return;
connection.connection.addListener(new InputStreamSender(stream.stream, 512){
int id;
@Override
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;
}
@Override
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.
}
});
}
@Override
public void hostServer(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 closeServer(){
connections.clear();
Threads.daemon(server::stop);
}
ArcConnection getByArcID(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(DcReason.error);
ArcConnection k = getByArcID(connection.getID());
if(k != null) connections.remove(k);
}
}
@Override
public void close(){
if(connection.isConnected()) connection.close(DcReason.closed);
}
}
private void handleException(Exception e){
if(e instanceof ArcNetException){
Core.app.post(() -> net.showError(new IOException("mismatch")));
}else if(e instanceof ClosedChannelException){
Core.app.post(() -> net.showError(new IOException("alreadyconnected")));
}else{
Core.app.post(() -> net.showError(e));
}
}
}

View File

@@ -1,204 +0,0 @@
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, DcReason reason){
ArcConnection k = getByArcID(connection.getID());
if(k == null) return;
Disconnect c = new Disconnect();
c.id = k.id;
c.reason = reason.toString();
Core.app.post(() -> {
Net.handleServerReceived(k.id, c);
connections.remove(k);
});
}
@Override
public void received(Connection connection, Object object){
ArcConnection k = getByArcID(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 sendServerStream(int id, Streamable stream){
ArcConnection connection = getByID(id);
if(connection == null) return;
connection.connection.addListener(new InputStreamSender(stream.stream, 512){
int id;
@Override
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;
}
@Override
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.
}
});
}
@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 getByArcID(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(DcReason.error);
ArcConnection k = getByArcID(connection.getID());
if(k != null) connections.remove(k);
}
}
@Override
public void close(){
if(connection.isConnected()) connection.close(DcReason.closed);
}
}
}

View File

@@ -19,9 +19,8 @@ import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.*; import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.Administration.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.plugin.Plugins; import io.anuke.mindustry.plugin.*;
import io.anuke.mindustry.plugin.Plugins.*; import io.anuke.mindustry.plugin.Plugins.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
@@ -169,7 +168,7 @@ public class ServerControl implements ApplicationListener{
}else{ }else{
netServer.kickAll(KickReason.gameover); netServer.kickAll(KickReason.gameover);
state.set(State.menu); state.set(State.menu);
Net.closeServer(); net.closeServer();
} }
}); });
@@ -203,12 +202,12 @@ public class ServerControl implements ApplicationListener{
handler.register("exit", "Exit the server application.", arg -> { handler.register("exit", "Exit the server application.", arg -> {
info("Shutting down server."); info("Shutting down server.");
Net.dispose(); net.dispose();
Core.app.exit(); Core.app.exit();
}); });
handler.register("stop", "Stop hosting the server.", arg -> { handler.register("stop", "Stop hosting the server.", arg -> {
Net.closeServer(); net.closeServer();
if(lastTask != null) lastTask.cancel(); if(lastTask != null) lastTask.cancel();
state.set(State.menu); state.set(State.menu);
info("Stopped server."); info("Stopped server.");
@@ -534,7 +533,7 @@ public class ServerControl implements ApplicationListener{
if(target != null){ if(target != null){
Call.sendMessage("[scarlet] " + target.name + " has been kicked by the server."); Call.sendMessage("[scarlet] " + target.name + " has been kicked by the server.");
netServer.kick(target.con.id, KickReason.kick); target.con.kick(KickReason.kick);
info("It is done."); info("It is done.");
}else{ }else{
info("Nobody with that name could be found..."); info("Nobody with that name could be found...");
@@ -563,7 +562,7 @@ public class ServerControl implements ApplicationListener{
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
if(netServer.admins.isIDBanned(player.uuid)){ if(netServer.admins.isIDBanned(player.uuid)){
Call.sendMessage("[scarlet] " + player.name + " has been banned."); Call.sendMessage("[scarlet] " + player.name + " has been banned.");
netServer.kick(player.con.id, KickReason.banned); player.con.kick(KickReason.banned);
} }
} }
}); });
@@ -830,7 +829,7 @@ public class ServerControl implements ApplicationListener{
r.run(); r.run();
}catch(MapException e){ }catch(MapException e){
Log.err(e.map.name() + ": " + e.getMessage()); Log.err(e.map.name() + ": " + e.getMessage());
Net.closeServer(); net.closeServer();
} }
} }
}; };
@@ -843,7 +842,7 @@ public class ServerControl implements ApplicationListener{
private void host(){ private void host(){
try{ try{
Net.host(Core.settings.getInt("port")); net.host(Core.settings.getInt("port"));
info("&lcOpened a server on port {0}.", Core.settings.getInt("port")); info("&lcOpened a server on port {0}.", Core.settings.getInt("port"));
}catch(BindException e){ }catch(BindException e){
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network."); Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");

View File

@@ -1,18 +1,18 @@
package io.anuke.mindustry.server; package io.anuke.mindustry.server;
import io.anuke.arc.backends.headless.HeadlessApplication; import io.anuke.arc.backends.headless.*;
import io.anuke.mindustry.*; import io.anuke.mindustry.*;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import static io.anuke.mindustry.Vars.platform;
public class ServerLauncher{ public class ServerLauncher{
public static void main(String[] args){ public static void main(String[] args){
try{ try{
Net.setClientProvider(new ArcNetClient()); Vars.platform = ArcNetImpl::new;
Net.setServerProvider(new ArcNetServer()); Vars.net = new Net(platform.getNet());
Vars.platform = new Platform(){};
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

@@ -14,6 +14,7 @@ import io.anuke.mindustry.entities.type.base.*;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -42,6 +43,7 @@ public class ApplicationTests{
@Override @Override
public void setup(){ public void setup(){
headless = true; headless = true;
net = new Net(null);
Vars.init(); Vars.init();
content.createContent(); content.createContent();