Merge branch 'master' of https://github.com/Anuken/Mindustry into 4.0
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
A pixelated sandbox tower defense game made using [LibGDX](https://libgdx.badlogicgames.com/). Winner of the [GDL Metal Monstrosity Jam](https://itch.io/jam/gdl---metal-monstrosity-jam).
|
A pixelated sandbox tower defense game made using [LibGDX](https://libgdx.badlogicgames.com/). Winner of the [GDL Metal Monstrosity Jam](https://itch.io/jam/gdl---metal-monstrosity-jam).
|
||||||
|
|
||||||
_[Issue tracker](https://waffle.io/Anuken/Mindustry)_
|
_[Issue tracker](https://waffle.io/Anuken/Mindustry)_
|
||||||
_[TODO list](TODO.md)_
|
_[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_
|
||||||
_[Wiki](http://mindustry.wikia.com/wiki/Mindustry_Wiki)_
|
_[Wiki](http://mindustry.wikia.com/wiki/Mindustry_Wiki)_
|
||||||
_[Discord](https://discord.gg/r8BkXNd)_
|
_[Discord](https://discord.gg/r8BkXNd)_
|
||||||
|
|
||||||
|
|||||||
86
TODO.md
86
TODO.md
@@ -1,86 +0,0 @@
|
|||||||
_Keep in mind that this is just a basic outline of planned features, and will be constantly changing!_
|
|
||||||
|
|
||||||
### Won't Add
|
|
||||||
_(These are not planned in the near future at all, and have been suggested before **many** times.)_
|
|
||||||
- Texture packs
|
|
||||||
- Online player profiles
|
|
||||||
- Player mech on Android
|
|
||||||
- Modding support
|
|
||||||
- Game speed increase (fast forward)
|
|
||||||
- Liquid teleporter
|
|
||||||
- More teleporter colors/any system that gives it more frequencies (numbers)
|
|
||||||
- More ore generation (if you're playing on maze, *play any different map, maze has the least resources, and for good reason!*)
|
|
||||||
- Power wires or conduits
|
|
||||||
|
|
||||||
### Already Suggested
|
|
||||||
_(not necessarily planned!)_
|
|
||||||
- "more blocks" "more turrets" "more content" "more X/Y/Z"
|
|
||||||
- Building of units (tanks, drones, _soldiers_, doesn't matter)
|
|
||||||
- Enemy bases, fighting against AI, capture points
|
|
||||||
- Co-op of any sort
|
|
||||||
- Campaign, challenge mode
|
|
||||||
- PvP mode
|
|
||||||
- Multiple cores, movable cores
|
|
||||||
- Movable turrets
|
|
||||||
- Batteries or storage for anything
|
|
||||||
- Destroy map indestructible blocks
|
|
||||||
- Customizable world ore generation + seed
|
|
||||||
- Steam release
|
|
||||||
- Research system, tech tree, persistent upgrades, upgrades at all
|
|
||||||
- Missile enemies/turrets/weapons (both homing and non-homing)
|
|
||||||
- 'Better graphics'
|
|
||||||
- Enemies dropping resources
|
|
||||||
- Final objectives/non-endless mode
|
|
||||||
- Fusion reactor
|
|
||||||
- Dams, flowing water
|
|
||||||
- Flying enemies
|
|
||||||
- Day/night cycle
|
|
||||||
- Solar panels
|
|
||||||
- Deflector shields
|
|
||||||
- Underground blocks
|
|
||||||
- Configurable outputs/inputs
|
|
||||||
- Getting items out of the core
|
|
||||||
- Map sharing/map browser
|
|
||||||
|
|
||||||
### Balance
|
|
||||||
- Slow down progression slightly
|
|
||||||
- Better endgame turrets (?)
|
|
||||||
- Nerf RTG, buff nuclear reactor
|
|
||||||
- Faster power
|
|
||||||
|
|
||||||
### Misc. QoL
|
|
||||||
- Minimap
|
|
||||||
- Underground conduits
|
|
||||||
- More indicators for core damaged/attacked
|
|
||||||
- Display playtime in saves
|
|
||||||
- New liquid conduit system
|
|
||||||
|
|
||||||
### Major Bugs
|
|
||||||
- Black screen when tabbing out on Android
|
|
||||||
- Random map reload when playing, leading to a crash (UI cause?)
|
|
||||||
- Google Payments verify crash
|
|
||||||
- Google Payments IllegalArgument crash
|
|
||||||
|
|
||||||
### Possible Additions
|
|
||||||
- Mech body upgrades
|
|
||||||
- Uranium extractor / uranium->iron converter
|
|
||||||
- Laser enemies
|
|
||||||
- Flying enemies that move in formation and spawn from different locations
|
|
||||||
- Fusion reactor
|
|
||||||
- Point defense turrets that take down projectiles
|
|
||||||
- Turrets fueled by lava
|
|
||||||
- Gas transporation and use
|
|
||||||
- Better enemy effects and looks
|
|
||||||
- Homing missile enemies and turrets
|
|
||||||
- Reflective shield blocks
|
|
||||||
- Spawn points changed into enemy bases with hostile turrets
|
|
||||||
- Unit production
|
|
||||||
|
|
||||||
### Optimization
|
|
||||||
- Optimize enemy + bullet code and check quadtree leaf parameters
|
|
||||||
- Optimize generator laser distribution, especially finding targets
|
|
||||||
- Optimize UI
|
|
||||||
- Check memory usage and GC, profile
|
|
||||||
- Optimize health bars and enemies in general
|
|
||||||
- Make drawing of enemies more efficient (don't call `flush()`?)
|
|
||||||
|
|
||||||
@@ -6,15 +6,18 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings.Secure;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import com.badlogic.gdx.backends.android.AndroidApplication;
|
import com.badlogic.gdx.backends.android.AndroidApplication;
|
||||||
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
|
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
|
||||||
|
import com.badlogic.gdx.utils.Base64Coder;
|
||||||
import io.anuke.kryonet.DefaultThreadImpl;
|
import io.anuke.kryonet.DefaultThreadImpl;
|
||||||
import io.anuke.kryonet.KryoClient;
|
import io.anuke.kryonet.KryoClient;
|
||||||
import io.anuke.kryonet.KryoServer;
|
import io.anuke.kryonet.KryoServer;
|
||||||
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
|
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
|
||||||
import io.anuke.mindustry.io.Platform;
|
import io.anuke.mindustry.io.Platform;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.ucore.core.Settings;
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
import io.anuke.ucore.scene.ui.TextField;
|
||||||
import io.anuke.ucore.scene.ui.layout.Unit;
|
import io.anuke.ucore.scene.ui.layout.Unit;
|
||||||
|
|
||||||
@@ -23,6 +26,7 @@ import java.text.NumberFormat;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
public class AndroidLauncher extends AndroidApplication{
|
public class AndroidLauncher extends AndroidApplication{
|
||||||
boolean doubleScaleTablets = true;
|
boolean doubleScaleTablets = true;
|
||||||
@@ -97,6 +101,40 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
public boolean isDebug() {
|
public boolean isDebug() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getUUID() {
|
||||||
|
try {
|
||||||
|
String s = Secure.getString(getContext().getContentResolver(),
|
||||||
|
Secure.ANDROID_ID);
|
||||||
|
|
||||||
|
if(s == null){
|
||||||
|
Settings.defaults("uuid", "");
|
||||||
|
|
||||||
|
String uuid = Settings.getString("uuid");
|
||||||
|
if(uuid.isEmpty()){
|
||||||
|
byte[] result = new byte[8];
|
||||||
|
new Random().nextBytes(result);
|
||||||
|
uuid = new String(Base64Coder.encode(result));
|
||||||
|
Settings.putString("uuid", uuid);
|
||||||
|
Settings.save();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Base64Coder.decode(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = s.length();
|
||||||
|
byte[] data = new byte[len / 2];
|
||||||
|
for (int i = 0; i < len; i += 2) {
|
||||||
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||||
|
+ Character.digit(s.charAt(i + 1), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}catch (Exception e){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(doubleScaleTablets && isTablet(this.getContext())){
|
if(doubleScaleTablets && isTablet(this.getContext())){
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ allprojects {
|
|||||||
appName = 'Mindustry'
|
appName = 'Mindustry'
|
||||||
gdxVersion = '1.9.8'
|
gdxVersion = '1.9.8'
|
||||||
aiVersion = '1.8.1'
|
aiVersion = '1.8.1'
|
||||||
uCoreVersion = '7f46315'
|
uCoreVersion = '21f04f8'
|
||||||
|
|
||||||
getVersionString = {
|
getVersionString = {
|
||||||
String buildVersion = getBuildVersion()
|
String buildVersion = getBuildVersion()
|
||||||
|
|||||||
@@ -41,12 +41,15 @@ text.server.friendlyfire=Friendly Fire
|
|||||||
text.trace=Trace Player
|
text.trace=Trace Player
|
||||||
text.trace.playername=Player name: [accent]{0}
|
text.trace.playername=Player name: [accent]{0}
|
||||||
text.trace.ip=IP: [accent]{0}
|
text.trace.ip=IP: [accent]{0}
|
||||||
|
text.trace.id=Unique ID: [accent]{0}
|
||||||
|
text.trace.android=Android Client: [accent]{0}
|
||||||
text.trace.modclient=Custom Client: [accent]{0}
|
text.trace.modclient=Custom Client: [accent]{0}
|
||||||
text.trace.totalblocksbroken=Total blocks broken: [accent]{0}
|
text.trace.totalblocksbroken=Total blocks broken: [accent]{0}
|
||||||
text.trace.structureblocksbroken=Structure blocks broken: [accent]{0}
|
text.trace.structureblocksbroken=Structure blocks broken: [accent]{0}
|
||||||
text.trace.lastblockbroken=Last block broken: [accent]{0}
|
text.trace.lastblockbroken=Last block broken: [accent]{0}
|
||||||
text.trace.totalblocksplaced=Total blocks placed: [accent]{0}
|
text.trace.totalblocksplaced=Total blocks placed: [accent]{0}
|
||||||
text.trace.lastblockplaced=Last block placed: [accent]{0}
|
text.trace.lastblockplaced=Last block placed: [accent]{0}
|
||||||
|
text.invalidid=Invalid client ID! Submit a bug report.
|
||||||
text.server.bans=Bans
|
text.server.bans=Bans
|
||||||
text.server.bans.none=No banned players found!
|
text.server.bans.none=No banned players found!
|
||||||
text.server.admins=Admins
|
text.server.admins=Admins
|
||||||
@@ -55,6 +58,10 @@ text.server.add=Add Server
|
|||||||
text.server.delete=Are you sure you want to delete this server?
|
text.server.delete=Are you sure you want to delete this server?
|
||||||
text.server.hostname=Host: {0}
|
text.server.hostname=Host: {0}
|
||||||
text.server.edit=Edit Server
|
text.server.edit=Edit Server
|
||||||
|
text.server.outdated=[crimson]Outdated Server![]
|
||||||
|
text.server.outdated.client=[crimson]Outdated Client![]
|
||||||
|
text.server.version=[lightgray]Version: {0}
|
||||||
|
text.server.custombuild=[yellow]Custom Build
|
||||||
text.confirmban=Are you sure you want to ban this player?
|
text.confirmban=Are you sure you want to ban this player?
|
||||||
text.confirmunban=Are you sure you want to unban this player?
|
text.confirmunban=Are you sure you want to unban this player?
|
||||||
text.confirmadmin=Are you sure you want to make this player an admin?
|
text.confirmadmin=Are you sure you want to make this player an admin?
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package io.anuke.mindustry.core;
|
package io.anuke.mindustry.core;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.utils.IntMap;
|
import com.badlogic.gdx.utils.IntMap;
|
||||||
import com.badlogic.gdx.utils.IntSet;
|
import com.badlogic.gdx.utils.IntSet;
|
||||||
@@ -67,11 +66,20 @@ public class NetClient extends Module {
|
|||||||
c.name = player.name;
|
c.name = player.name;
|
||||||
c.android = android;
|
c.android = android;
|
||||||
c.color = Color.rgba8888(player.color);
|
c.color = Color.rgba8888(player.color);
|
||||||
|
c.uuid = Platform.instance.getUUID();
|
||||||
|
|
||||||
|
if(c.uuid == null){
|
||||||
|
ui.showError("$text.invalidid");
|
||||||
|
ui.loadfrag.hide();
|
||||||
|
disconnectQuietly();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Net.send(c, SendMode.tcp);
|
Net.send(c, SendMode.tcp);
|
||||||
|
|
||||||
Timers.runTask(dataTimeout, () -> {
|
Timers.runTask(dataTimeout, () -> {
|
||||||
if (!gotData) {
|
if (!gotData) {
|
||||||
Gdx.app.error("Mindustry", "Failed to load data!");
|
Log.err("Failed to load data!");
|
||||||
ui.loadfrag.hide();
|
ui.loadfrag.hide();
|
||||||
Net.disconnect();
|
Net.disconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,18 +51,27 @@ public class NetServer extends Module{
|
|||||||
Events.on(GameOverEvent.class, () -> weapons.clear());
|
Events.on(GameOverEvent.class, () -> weapons.clear());
|
||||||
|
|
||||||
Net.handleServer(Connect.class, (id, connect) -> {
|
Net.handleServer(Connect.class, (id, connect) -> {
|
||||||
if(admins.isBanned(connect.addressTCP)){
|
if(admins.isIPBanned(connect.addressTCP)){
|
||||||
Net.kickConnection(id, KickReason.banned);
|
Net.kickConnection(id, KickReason.banned);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
||||||
|
String uuid = new String(Base64Coder.encode(packet.uuid));
|
||||||
if(Net.getConnection(id) == null ||
|
if(Net.getConnection(id) == null ||
|
||||||
admins.isBanned(Net.getConnection(id).address)) return;
|
admins.isIPBanned(Net.getConnection(id).address)) return;
|
||||||
|
|
||||||
|
if(admins.isIDBanned(uuid)){
|
||||||
|
Net.kickConnection(id, KickReason.banned);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String ip = Net.getConnection(id).address;
|
String ip = Net.getConnection(id).address;
|
||||||
|
|
||||||
admins.setKnownName(ip, packet.name);
|
admins.setKnownName(ip, packet.name);
|
||||||
|
admins.setKnownIP(uuid, ip);
|
||||||
|
admins.getTrace(ip).uuid = uuid;
|
||||||
|
admins.getTrace(ip).android = packet.android;
|
||||||
|
|
||||||
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
|
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
|
||||||
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||||
@@ -270,7 +279,7 @@ public class NetServer extends Module{
|
|||||||
String ip = Net.getConnection(other.clientid).address;
|
String ip = Net.getConnection(other.clientid).address;
|
||||||
|
|
||||||
if(packet.action == AdminAction.ban){
|
if(packet.action == AdminAction.ban){
|
||||||
admins.banPlayer(ip);
|
admins.banPlayerIP(ip);
|
||||||
Net.kickConnection(other.clientid, KickReason.banned);
|
Net.kickConnection(other.clientid, 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(packet.action == AdminAction.kick){
|
}else if(packet.action == AdminAction.kick){
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import io.anuke.mindustry.entities.enemies.Enemy;
|
|||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.ucore.entities.BulletEntity;
|
import io.anuke.ucore.entities.BulletEntity;
|
||||||
import io.anuke.ucore.entities.Entity;
|
import io.anuke.ucore.entities.Entity;
|
||||||
import io.anuke.ucore.entities.SolidEntity;
|
|
||||||
import io.anuke.ucore.util.Timer;
|
import io.anuke.ucore.util.Timer;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
@@ -65,11 +64,6 @@ public class Bullet extends BulletEntity{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collides(SolidEntity other){
|
|
||||||
return (other instanceof Player) == (owner instanceof Enemy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDamage(){
|
public int getDamage(){
|
||||||
return damage == -1 ? type.damage : damage;
|
return damage == -1 ? type.damage : damage;
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ public class DesktopInput extends InputHandler{
|
|||||||
private boolean beganBreak;
|
private boolean beganBreak;
|
||||||
private boolean rotated = false, rotatedAlt, zoomed;
|
private boolean rotated = false, rotatedAlt, zoomed;
|
||||||
|
|
||||||
@Override public float getCursorEndX(){ return endx; }
|
@Override public float getCursorEndX(){ return select() ? getCursorX() : endx; }
|
||||||
@Override public float getCursorEndY(){ return endy; }
|
@Override public float getCursorEndY(){ return select() ? getCursorY() : endy; }
|
||||||
@Override public float getCursorX(){ return (int)(Graphics.screen(mousex, mousey).x + 2); }
|
@Override public float getCursorX(){ return (int)(Graphics.screen(mousex, mousey).x); }
|
||||||
@Override public float getCursorY(){ return (int)(Gdx.graphics.getHeight() - 1 - Graphics.screen(mousex, mousey).y); }
|
@Override public float getCursorY(){ return (int)(Gdx.graphics.getHeight() - Graphics.screen(mousex, mousey).y); }
|
||||||
@Override public boolean drawPlace(){ return !beganBreak; }
|
@Override public boolean drawPlace(){ return !beganBreak; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,6 +156,10 @@ public class DesktopInput extends InputHandler{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean select(){
|
||||||
|
return !Inputs.keyDown("select") && !Inputs.keyRelease("select");
|
||||||
|
}
|
||||||
|
|
||||||
public int tilex(){
|
public int tilex(){
|
||||||
return (recipe != null && recipe.result.isMultiblock() &&
|
return (recipe != null && recipe.result.isMultiblock() &&
|
||||||
recipe.result.size % 2 == 0) ?
|
recipe.result.size % 2 == 0) ?
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public abstract class Platform {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public boolean isDebug(){return false;}
|
public boolean isDebug(){return false;}
|
||||||
|
/**Must be 8 bytes in length.*/
|
||||||
|
public byte[] getUUID(){return null;}
|
||||||
public ThreadProvider getThreadProvider(){
|
public ThreadProvider getThreadProvider(){
|
||||||
return new ThreadProvider() {
|
return new ThreadProvider() {
|
||||||
@Override public boolean isOnThread() {return true;}
|
@Override public boolean isOnThread() {return true;}
|
||||||
|
|||||||
@@ -7,16 +7,20 @@ import io.anuke.ucore.core.Settings;
|
|||||||
|
|
||||||
public class Administration {
|
public class Administration {
|
||||||
private Json json = new Json();
|
private Json json = new Json();
|
||||||
private Array<String> bannedIPS = new Array<>();
|
private Array<String> bannedIPs = new Array<>();
|
||||||
|
private Array<String> bannedIDs = new Array<>();
|
||||||
private Array<String> admins = new Array<>();
|
private Array<String> admins = new Array<>();
|
||||||
private ObjectMap<String, String> known = new ObjectMap<>();
|
private ObjectMap<String, String> ipNames = new ObjectMap<>();
|
||||||
|
private ObjectMap<String, String> idIPs = new ObjectMap<>();
|
||||||
private ObjectMap<String, TraceInfo> traces = new ObjectMap<>();
|
private ObjectMap<String, TraceInfo> traces = new ObjectMap<>();
|
||||||
|
|
||||||
public Administration(){
|
public Administration(){
|
||||||
Settings.defaultList(
|
Settings.defaultList(
|
||||||
"bans", "{}",
|
"bans", "{}",
|
||||||
|
"bannedIDs", "{}",
|
||||||
"admins", "{}",
|
"admins", "{}",
|
||||||
"knownIPs", "{}"
|
"knownIPs", "{}",
|
||||||
|
"knownIDs", "{}"
|
||||||
);
|
);
|
||||||
|
|
||||||
load();
|
load();
|
||||||
@@ -34,35 +38,81 @@ public class Administration {
|
|||||||
|
|
||||||
/**Sets last known name for an IP.*/
|
/**Sets last known name for an IP.*/
|
||||||
public void setKnownName(String ip, String name){
|
public void setKnownName(String ip, String name){
|
||||||
known.put(ip, name);
|
ipNames.put(ip, name);
|
||||||
|
saveKnown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Sets last known UUID for an IP.*/
|
||||||
|
public void setKnownIP(String id, String ip){
|
||||||
|
idIPs.put(id, ip);
|
||||||
saveKnown();
|
saveKnown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/
|
/**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/
|
||||||
public String getLastName(String ip){
|
public String getLastName(String ip){
|
||||||
return known.get(ip, "unknown");
|
return ipNames.get(ip, "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Returns the last known IP for a UUID. Returns 'unknown' if this IP has an unknown IP.*/
|
||||||
|
public String getLastIP(String id){
|
||||||
|
return idIPs.get(id, "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Return the last known device ID associated with an IP. Returns 'unknown' if this IP has an unknown device.*/
|
||||||
|
public String getLastID(String ip){
|
||||||
|
for(String id : idIPs.keys()){
|
||||||
|
if(idIPs.get(id).equals(ip)){
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Returns list of banned IPs.*/
|
/**Returns list of banned IPs.*/
|
||||||
public Array<String> getBanned(){
|
public Array<String> getBanned(){
|
||||||
return bannedIPS;
|
return bannedIPs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Returns list of banned IDs.*/
|
||||||
|
public Array<String> getBannedIDs(){
|
||||||
|
return bannedIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Bans a player by IP; returns whether this player was already banned.*/
|
/**Bans a player by IP; returns whether this player was already banned.*/
|
||||||
public boolean banPlayer(String ip){
|
public boolean banPlayerIP(String ip){
|
||||||
if(bannedIPS.contains(ip, false))
|
if(bannedIPs.contains(ip, false))
|
||||||
return false;
|
return false;
|
||||||
bannedIPS.add(ip);
|
bannedIPs.add(ip);
|
||||||
|
saveBans();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Bans a player by UUID.*/
|
||||||
|
public boolean banPlayerID(String id){
|
||||||
|
if(bannedIDs.contains(id, false))
|
||||||
|
return false;
|
||||||
|
bannedIDs.add(id);
|
||||||
saveBans();
|
saveBans();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Unbans a player by IP; returns whether this player was banned in the first place..*/
|
/**Unbans a player by IP; returns whether this player was banned in the first place..*/
|
||||||
public boolean unbanPlayer(String ip){
|
public boolean unbanPlayerIP(String ip){
|
||||||
if(!bannedIPS.contains(ip, false))
|
if(!bannedIPs.contains(ip, false))
|
||||||
return false;
|
return false;
|
||||||
bannedIPS.removeValue(ip, false);
|
bannedIPs.removeValue(ip, false);
|
||||||
|
saveBans();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Unbans a player by IP; returns whether this player was banned in the first place..*/
|
||||||
|
public boolean unbanPlayerID(String ip){
|
||||||
|
if(!bannedIDs.contains(ip, false))
|
||||||
|
return false;
|
||||||
|
bannedIDs.removeValue(ip, false);
|
||||||
saveBans();
|
saveBans();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -93,8 +143,12 @@ public class Administration {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBanned(String ip){
|
public boolean isIPBanned(String ip){
|
||||||
return bannedIPS.contains(ip, false);
|
return bannedIPs.contains(ip, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIDBanned(String uuid){
|
||||||
|
return bannedIDs.contains(uuid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdmin(String ip){
|
public boolean isAdmin(String ip){
|
||||||
@@ -102,12 +156,14 @@ public class Administration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void saveKnown(){
|
private void saveKnown(){
|
||||||
Settings.putString("knownIPs", json.toJson(known));
|
Settings.putString("knownIPs", json.toJson(ipNames));
|
||||||
|
Settings.putString("knownIDs", json.toJson(idIPs));
|
||||||
Settings.save();
|
Settings.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveBans(){
|
private void saveBans(){
|
||||||
Settings.putString("bans", json.toJson(bannedIPS));
|
Settings.putString("bans", json.toJson(bannedIPs));
|
||||||
|
Settings.putString("bannedIDs", json.toJson(bannedIDs));
|
||||||
Settings.save();
|
Settings.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +173,11 @@ public class Administration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void load(){
|
private void load(){
|
||||||
bannedIPS = json.fromJson(Array.class, Settings.getString("bans"));
|
bannedIPs = json.fromJson(Array.class, Settings.getString("bans"));
|
||||||
|
bannedIDs = json.fromJson(Array.class, Settings.getString("bannedIDs"));
|
||||||
admins = json.fromJson(Array.class, Settings.getString("admins"));
|
admins = json.fromJson(Array.class, Settings.getString("admins"));
|
||||||
known = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
|
ipNames = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
|
||||||
|
idIPs = json.fromJson(ObjectMap.class, Settings.getString("knownIDs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ public class Host {
|
|||||||
public final String mapname;
|
public final String mapname;
|
||||||
public final int wave;
|
public final int wave;
|
||||||
public final int players;
|
public final int players;
|
||||||
|
public final int version;
|
||||||
|
|
||||||
public Host(String name, String address, String mapname, int wave, int players){
|
public Host(String name, String address, String mapname, int wave, int players, int version){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.players = players;
|
this.players = players;
|
||||||
this.mapname = mapname;
|
this.mapname = mapname;
|
||||||
this.wave = wave;
|
this.wave = wave;
|
||||||
|
this.version = version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.ByteArray;
|
|||||||
import com.badlogic.gdx.utils.TimeUtils;
|
import com.badlogic.gdx.utils.TimeUtils;
|
||||||
import io.anuke.mindustry.entities.Player;
|
import io.anuke.mindustry.entities.Player;
|
||||||
import io.anuke.mindustry.game.GameMode;
|
import io.anuke.mindustry.game.GameMode;
|
||||||
|
import io.anuke.mindustry.io.Version;
|
||||||
import io.anuke.mindustry.resource.Upgrade;
|
import io.anuke.mindustry.resource.Upgrade;
|
||||||
import io.anuke.mindustry.resource.Weapon;
|
import io.anuke.mindustry.resource.Weapon;
|
||||||
import io.anuke.mindustry.world.*;
|
import io.anuke.mindustry.world.*;
|
||||||
@@ -16,6 +17,7 @@ import io.anuke.ucore.core.Timers;
|
|||||||
import io.anuke.ucore.entities.Entities;
|
import io.anuke.ucore.entities.Entities;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -342,4 +344,46 @@ public class NetworkIO {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ByteBuffer writeServerData(){
|
||||||
|
int maxlen = 32;
|
||||||
|
|
||||||
|
String host = (headless ? "Server" : player.name);
|
||||||
|
String map = world.getMap().name;
|
||||||
|
|
||||||
|
host = host.substring(0, Math.min(host.length(), maxlen));
|
||||||
|
map = map.substring(0, Math.min(map.length(), maxlen));
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(128);
|
||||||
|
|
||||||
|
buffer.put((byte)host.getBytes().length);
|
||||||
|
buffer.put(host.getBytes());
|
||||||
|
|
||||||
|
buffer.put((byte)map.getBytes().length);
|
||||||
|
buffer.put(map.getBytes());
|
||||||
|
|
||||||
|
buffer.putInt(playerGroup.size());
|
||||||
|
buffer.putInt(state.wave);
|
||||||
|
buffer.putInt(Version.build);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Host readServerData(String hostAddress, ByteBuffer buffer){
|
||||||
|
byte hlength = buffer.get();
|
||||||
|
byte[] hb = new byte[hlength];
|
||||||
|
buffer.get(hb);
|
||||||
|
|
||||||
|
byte mlength = buffer.get();
|
||||||
|
byte[] mb = new byte[mlength];
|
||||||
|
buffer.get(mb);
|
||||||
|
|
||||||
|
String host = new String(hb);
|
||||||
|
String map = new String(mb);
|
||||||
|
|
||||||
|
int players = buffer.getInt();
|
||||||
|
int wave = buffer.getInt();
|
||||||
|
int version = buffer.getInt();
|
||||||
|
|
||||||
|
return new Host(host, hostAddress, map, wave, players, version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.anuke.mindustry.net;
|
package io.anuke.mindustry.net;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Base64Coder;
|
||||||
import com.badlogic.gdx.utils.reflect.ClassReflection;
|
import com.badlogic.gdx.utils.reflect.ClassReflection;
|
||||||
import com.badlogic.gdx.utils.reflect.ReflectionException;
|
import com.badlogic.gdx.utils.reflect.ReflectionException;
|
||||||
import io.anuke.mindustry.entities.Player;
|
import io.anuke.mindustry.entities.Player;
|
||||||
@@ -56,6 +57,7 @@ public class Packets {
|
|||||||
public String name;
|
public String name;
|
||||||
public boolean android;
|
public boolean android;
|
||||||
public int color;
|
public int color;
|
||||||
|
public byte[] uuid;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer buffer) {
|
public void write(ByteBuffer buffer) {
|
||||||
@@ -64,6 +66,7 @@ public class Packets {
|
|||||||
buffer.put(name.getBytes());
|
buffer.put(name.getBytes());
|
||||||
buffer.put(android ? (byte)1 : 0);
|
buffer.put(android ? (byte)1 : 0);
|
||||||
buffer.putInt(color);
|
buffer.putInt(color);
|
||||||
|
buffer.put(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,6 +78,8 @@ public class Packets {
|
|||||||
name = new String(bytes);
|
name = new String(bytes);
|
||||||
android = buffer.get() == 1;
|
android = buffer.get() == 1;
|
||||||
color = buffer.getInt();
|
color = buffer.getInt();
|
||||||
|
uuid = new byte[8];
|
||||||
|
buffer.get(uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,6 +574,7 @@ public class Packets {
|
|||||||
buffer.putShort((short)info.ip.getBytes().length);
|
buffer.putShort((short)info.ip.getBytes().length);
|
||||||
buffer.put(info.ip.getBytes());
|
buffer.put(info.ip.getBytes());
|
||||||
buffer.put(info.modclient ? (byte)1 : 0);
|
buffer.put(info.modclient ? (byte)1 : 0);
|
||||||
|
buffer.put(info.android ? (byte)1 : 0);
|
||||||
|
|
||||||
buffer.putInt(info.totalBlocksBroken);
|
buffer.putInt(info.totalBlocksBroken);
|
||||||
buffer.putInt(info.structureBlocksBroken);
|
buffer.putInt(info.structureBlocksBroken);
|
||||||
@@ -576,6 +582,7 @@ public class Packets {
|
|||||||
|
|
||||||
buffer.putInt(info.totalBlocksPlaced);
|
buffer.putInt(info.totalBlocksPlaced);
|
||||||
buffer.putInt(info.lastBlockPlaced.id);
|
buffer.putInt(info.lastBlockPlaced.id);
|
||||||
|
buffer.put(Base64Coder.decode(info.uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -589,11 +596,16 @@ public class Packets {
|
|||||||
|
|
||||||
info.playerid = id;
|
info.playerid = id;
|
||||||
info.modclient = buffer.get() == 1;
|
info.modclient = buffer.get() == 1;
|
||||||
|
info.android = buffer.get() == 1;
|
||||||
info.totalBlocksBroken = buffer.getInt();
|
info.totalBlocksBroken = buffer.getInt();
|
||||||
info.structureBlocksBroken = buffer.getInt();
|
info.structureBlocksBroken = buffer.getInt();
|
||||||
info.lastBlockBroken = Block.getByID(buffer.getInt());
|
info.lastBlockBroken = Block.getByID(buffer.getInt());
|
||||||
info.totalBlocksPlaced = buffer.getInt();
|
info.totalBlocksPlaced = buffer.getInt();
|
||||||
info.lastBlockPlaced = Block.getByID(buffer.getInt());
|
info.lastBlockPlaced = Block.getByID(buffer.getInt());
|
||||||
|
byte[] uuid = new byte[8];
|
||||||
|
buffer.get(uuid);
|
||||||
|
|
||||||
|
info.uuid = new String(Base64Coder.encode(uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.TimeUtils;
|
|||||||
import com.badlogic.gdx.utils.reflect.ClassReflection;
|
import com.badlogic.gdx.utils.reflect.ClassReflection;
|
||||||
import io.anuke.mindustry.entities.Player;
|
import io.anuke.mindustry.entities.Player;
|
||||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
import io.anuke.mindustry.net.Packets.Disconnect;
|
||||||
|
import io.anuke.ucore.util.Log;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.playerGroup;
|
import static io.anuke.mindustry.Vars.playerGroup;
|
||||||
|
|
||||||
@@ -13,12 +14,16 @@ public class ServerDebug {
|
|||||||
private IntMap<OrderedMap<Class<?>, Long>> last = new IntMap<>();
|
private IntMap<OrderedMap<Class<?>, Long>> last = new IntMap<>();
|
||||||
|
|
||||||
public void handle(int connection, Object packet){
|
public void handle(int connection, Object packet){
|
||||||
if(!last.containsKey(connection))
|
try {
|
||||||
last.put(connection, new OrderedMap<>());
|
if (!last.containsKey(connection))
|
||||||
if(packet instanceof Disconnect)
|
last.put(connection, new OrderedMap<>());
|
||||||
last.remove(connection);
|
if (packet instanceof Disconnect)
|
||||||
else
|
last.remove(connection);
|
||||||
last.get(connection).put(packet.getClass(), TimeUtils.millis());
|
else
|
||||||
|
last.get(connection).put(packet.getClass(), TimeUtils.millis());
|
||||||
|
}catch (Exception e){
|
||||||
|
Log.err("<An internal debug error has occurred.>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getOut(){
|
public String getOut(){
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public class TraceInfo {
|
|||||||
public int playerid;
|
public int playerid;
|
||||||
public String ip;
|
public String ip;
|
||||||
public boolean modclient;
|
public boolean modclient;
|
||||||
|
public boolean android;
|
||||||
|
|
||||||
public int totalBlocksBroken;
|
public int totalBlocksBroken;
|
||||||
public int structureBlocksBroken;
|
public int structureBlocksBroken;
|
||||||
@@ -15,6 +16,8 @@ public class TraceInfo {
|
|||||||
public int totalBlocksPlaced;
|
public int totalBlocksPlaced;
|
||||||
public Block lastBlockPlaced = Blocks.air;
|
public Block lastBlockPlaced = Blocks.air;
|
||||||
|
|
||||||
|
public String uuid;
|
||||||
|
|
||||||
public TraceInfo(String ip){
|
public TraceInfo(String ip){
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class BansDialog extends FloatingDialog {
|
|||||||
res.add().growX();
|
res.add().growX();
|
||||||
res.addImageButton("icon-cancel", 14*3, () -> {
|
res.addImageButton("icon-cancel", 14*3, () -> {
|
||||||
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
|
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
|
||||||
netServer.admins.unbanPlayer(ip);
|
netServer.admins.unbanPlayerIP(ip);
|
||||||
setup();
|
setup();
|
||||||
});
|
});
|
||||||
}).size(h).pad(-14f);
|
}).size(h).pad(-14f);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package io.anuke.mindustry.ui.dialogs;
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
|
import com.badlogic.gdx.math.MathUtils;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.io.Platform;
|
import io.anuke.mindustry.io.Platform;
|
||||||
|
import io.anuke.mindustry.io.Version;
|
||||||
import io.anuke.mindustry.net.Host;
|
import io.anuke.mindustry.net.Host;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.ucore.core.Settings;
|
import io.anuke.ucore.core.Settings;
|
||||||
@@ -13,6 +15,7 @@ import io.anuke.ucore.scene.ui.Dialog;
|
|||||||
import io.anuke.ucore.scene.ui.ImageButton;
|
import io.anuke.ucore.scene.ui.ImageButton;
|
||||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||||
import io.anuke.ucore.scene.ui.TextButton;
|
import io.anuke.ucore.scene.ui.TextButton;
|
||||||
|
import io.anuke.ucore.scene.ui.layout.Cell;
|
||||||
import io.anuke.ucore.scene.ui.layout.Table;
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
import io.anuke.ucore.util.Bundles;
|
import io.anuke.ucore.util.Bundles;
|
||||||
import io.anuke.ucore.util.Log;
|
import io.anuke.ucore.util.Log;
|
||||||
@@ -84,7 +87,7 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
|
|
||||||
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
|
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
|
||||||
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
|
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
|
||||||
}).width(w).height(140f).pad(4f).get();
|
}).width(w).height(150f).pad(4f).get();
|
||||||
|
|
||||||
button.getLabel().setWrap(true);
|
button.getLabel().setWrap(true);
|
||||||
|
|
||||||
@@ -131,16 +134,37 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
server.content.label(() -> Bundles.get("text.server.refreshing") + Strings.animated(4, 11, "."));
|
server.content.label(() -> Bundles.get("text.server.refreshing") + Strings.animated(4, 11, "."));
|
||||||
|
|
||||||
Net.pingHost(server.ip, server.port, host -> {
|
Net.pingHost(server.ip, server.port, host -> {
|
||||||
|
String versionString;
|
||||||
|
|
||||||
|
if(host.version == -1) {
|
||||||
|
versionString = Bundles.format("text.server.version", Bundles.get("text.server.custombuild"));
|
||||||
|
}else if(host.version == 0){
|
||||||
|
versionString = Bundles.get("text.server.outdated");
|
||||||
|
}else if(host.version < Version.build && Version.build != -1){
|
||||||
|
versionString = Bundles.get("text.server.outdated") + "\n" +
|
||||||
|
Bundles.format("text.server.version", host.version);
|
||||||
|
}else if(host.version > Version.build && Version.build != -1){
|
||||||
|
versionString = Bundles.get("text.server.outdated.client") + "\n" +
|
||||||
|
Bundles.format("text.server.version", host.version);
|
||||||
|
}else{
|
||||||
|
versionString = Bundles.format("text.server.version", host.version);
|
||||||
|
}
|
||||||
|
|
||||||
server.content.clear();
|
server.content.clear();
|
||||||
|
|
||||||
server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left();
|
server.content.table(t -> {
|
||||||
server.content.row();
|
t.add(versionString).left();
|
||||||
server.content.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
|
t.row();
|
||||||
Bundles.format("text.players.single", host.players))).left();
|
t.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left();
|
||||||
server.content.row();
|
t.row();
|
||||||
server.content.add("[lightgray]" + Bundles.format("text.save.map", host.mapname)).left();
|
t.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
|
||||||
server.content.row();
|
Bundles.format("text.players.single", host.players))).left();
|
||||||
server.content.add("[lightgray]" + Bundles.format("text.save.wave", host.wave)).left();
|
t.row();
|
||||||
|
t.add("[lightgray]" + Bundles.format("text.save.map", host.mapname) + " / " + Bundles.format("text.save.wave", host.wave)).left();
|
||||||
|
}).expand().left().bottom().padLeft(12f).padBottom(8);
|
||||||
|
|
||||||
|
//server.content.add(versionString).top().expandY().top().expandX();
|
||||||
|
|
||||||
}, e -> {
|
}, e -> {
|
||||||
server.content.clear();
|
server.content.clear();
|
||||||
server.content.add("$text.host.invalid");
|
server.content.add("$text.host.invalid");
|
||||||
@@ -161,7 +185,7 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
|
|
||||||
hosts.add(remote).growX();
|
hosts.add(remote).growX();
|
||||||
hosts.row();
|
hosts.row();
|
||||||
hosts.add(local).growX();
|
hosts.add(local).width(w);
|
||||||
|
|
||||||
ScrollPane pane = new ScrollPane(hosts, "clear");
|
ScrollPane pane = new ScrollPane(hosts, "clear");
|
||||||
pane.setFadeScrollBars(false);
|
pane.setFadeScrollBars(false);
|
||||||
@@ -188,15 +212,29 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
});
|
});
|
||||||
}).size(50f, 54f).get();
|
}).size(50f, 54f).get();
|
||||||
button.update(() -> button.getStyle().imageUpColor = player.getColor());
|
button.update(() -> button.getStyle().imageUpColor = player.getColor());
|
||||||
|
|
||||||
}).width(w).height(70f).pad(4);
|
}).width(w).height(70f).pad(4);
|
||||||
content().row();
|
content().row();
|
||||||
content().add(pane).width(w).pad(0);
|
content().add(pane).width(w + 34).pad(0);
|
||||||
content().row();
|
content().row();
|
||||||
content().addCenteredImageTextButton("$text.server.add", "icon-add", "clear", 14*3, () -> {
|
content().addCenteredImageTextButton("$text.server.add", "icon-add", "clear", 14*3, () -> {
|
||||||
renaming = null;
|
renaming = null;
|
||||||
add.show();
|
add.show();
|
||||||
}).width(w).height(80f);
|
}).marginLeft(6).width(w).height(80f).update(button -> {
|
||||||
|
float pw = w;
|
||||||
|
float pad = 0f;
|
||||||
|
if(pane.getChildren().first().getPrefHeight() > pane.getHeight()){
|
||||||
|
pw = w + 30;
|
||||||
|
pad = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell<TextButton> cell = ((Table)pane.getParent()).getCell(button);
|
||||||
|
|
||||||
|
if(!MathUtils.isEqual(cell.getMinWidth(), pw)){
|
||||||
|
cell.width(pw);
|
||||||
|
cell.padLeft(pad);
|
||||||
|
pane.getParent().invalidateHierarchy();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLocalHosts(Array<Host> array){
|
void addLocalHosts(Array<Host> array){
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ public class TraceDialog extends FloatingDialog {
|
|||||||
table.row();
|
table.row();
|
||||||
table.add(Bundles.format("text.trace.ip", info.ip));
|
table.add(Bundles.format("text.trace.ip", info.ip));
|
||||||
table.row();
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.id", info.uuid));
|
||||||
|
table.row();
|
||||||
table.add(Bundles.format("text.trace.modclient", info.modclient));
|
table.add(Bundles.format("text.trace.modclient", info.modclient));
|
||||||
table.row();
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.android", info.android));
|
||||||
|
table.row();
|
||||||
|
|
||||||
table.add().pad(5);
|
table.add().pad(5);
|
||||||
table.row();
|
table.row();
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class PlayerListFragment implements Fragment{
|
|||||||
t.addImageButton("icon-ban", 14*2, () -> {
|
t.addImageButton("icon-ban", 14*2, () -> {
|
||||||
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
|
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
|
||||||
if(Net.server()) {
|
if(Net.server()) {
|
||||||
netServer.admins.banPlayer(connection.address);
|
netServer.admins.banPlayerIP(connection.address);
|
||||||
Net.kickConnection(player.clientid, KickReason.banned);
|
Net.kickConnection(player.clientid, KickReason.banned);
|
||||||
}else{
|
}else{
|
||||||
NetEvents.handleAdministerRequest(player, AdminAction.ban);
|
NetEvents.handleAdministerRequest(player, AdminAction.ban);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import io.anuke.mindustry.resource.Recipes;
|
|||||||
import io.anuke.mindustry.world.blocks.Blocks;
|
import io.anuke.mindustry.world.blocks.Blocks;
|
||||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||||
import io.anuke.ucore.core.Effects;
|
import io.anuke.ucore.core.Effects;
|
||||||
import io.anuke.ucore.core.Sounds;
|
|
||||||
import io.anuke.ucore.entities.Entities;
|
import io.anuke.ucore.entities.Entities;
|
||||||
import io.anuke.ucore.entities.SolidEntity;
|
import io.anuke.ucore.entities.SolidEntity;
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ public class Placement {
|
|||||||
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sound) threads.run(() -> Sounds.play("break"));
|
if(sound) threads.run(() -> Effects.sound("break", x * tilesize, y * tilesize));
|
||||||
|
|
||||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||||
tile.setBlock(Blocks.air);
|
tile.setBlock(Blocks.air);
|
||||||
@@ -86,7 +85,7 @@ public class Placement {
|
|||||||
}
|
}
|
||||||
}else if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
}else if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
||||||
|
|
||||||
if(effects && sound) threads.run(() -> Sounds.play("place"));
|
if(effects && sound) threads.run(() -> Effects.sound("place", x * tilesize, y * tilesize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean validPlace(int x, int y, Block type){
|
public static boolean validPlace(int x, int y, Block type){
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package io.anuke.mindustry.world;
|
|||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.graphics.Pixmap;
|
import com.badlogic.gdx.graphics.Pixmap;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.IntMap;
|
|
||||||
import com.badlogic.gdx.utils.ObjectMap;
|
import com.badlogic.gdx.utils.ObjectMap;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||||
@@ -95,15 +94,4 @@ public class WorldGenerator {
|
|||||||
|
|
||||||
return core;
|
return core;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IntMap<Block> map(Object...objects){
|
|
||||||
|
|
||||||
IntMap<Block> out = new IntMap<>();
|
|
||||||
|
|
||||||
for(int i = 0; i < objects.length; i += 2){
|
|
||||||
out.put(Hue.rgb((Color)objects[i]), (Block)objects[i+1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ public class Turret extends Block{
|
|||||||
if(Float.isNaN(entity.rotation)){
|
if(Float.isNaN(entity.rotation)){
|
||||||
entity.rotation = 0;
|
entity.rotation = 0;
|
||||||
}
|
}
|
||||||
entity.rotation = Mathf.slerp(entity.rotation, targetRot,
|
entity.rotation = Mathf.slerpDelta(entity.rotation, targetRot,
|
||||||
rotatespeed*Timers.delta());
|
rotatespeed);
|
||||||
|
|
||||||
if(Angles.angleDist(entity.rotation, targetRot) < shootCone && entity.timer.get(timerReload, reload)){
|
if(Angles.angleDist(entity.rotation, targetRot) < shootCone && entity.timer.get(timerReload, reload)){
|
||||||
if(shootsound != null && entity.timer.get(timerSound, soundReload)) Effects.sound(shootsound, entity);
|
if(shootsound != null && entity.timer.get(timerSound, soundReload)) Effects.sound(shootsound, entity);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import io.anuke.ucore.util.Strings;
|
|||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.syncBlockState;
|
import static io.anuke.mindustry.Vars.syncBlockState;
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ public class Teleporter extends PowerBlock{
|
|||||||
TeleporterEntity entity = tile.entity();
|
TeleporterEntity entity = tile.entity();
|
||||||
if(entity != null){
|
if(entity != null){
|
||||||
entity.color = data;
|
entity.color = data;
|
||||||
|
Arrays.fill(entity.items, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import io.anuke.ucore.UCore;
|
|||||||
import io.anuke.ucore.util.Strings;
|
import io.anuke.ucore.util.Strings;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
@@ -98,4 +100,21 @@ public class DesktopPlatform extends Platform {
|
|||||||
public ThreadProvider getThreadProvider() {
|
public ThreadProvider getThreadProvider() {
|
||||||
return new DefaultThreadImpl();
|
return new DefaultThreadImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getUUID() {
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
|
||||||
|
NetworkInterface out;
|
||||||
|
for(out = e.nextElement(); out.getHardwareAddress() == null && e.hasMoreElements(); out = e.nextElement());
|
||||||
|
|
||||||
|
byte[] bytes = out.getHardwareAddress();
|
||||||
|
byte[] result = new byte[8];
|
||||||
|
System.arraycopy(bytes, 0, result, 0, bytes.length);
|
||||||
|
return result;
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.backends.gwt.GwtApplication;
|
|||||||
import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration;
|
import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration;
|
||||||
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback;
|
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback;
|
||||||
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState;
|
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState;
|
||||||
|
import com.badlogic.gdx.utils.Base64Coder;
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.dom.client.Document;
|
import com.google.gwt.dom.client.Document;
|
||||||
import com.google.gwt.dom.client.Element;
|
import com.google.gwt.dom.client.Element;
|
||||||
@@ -17,8 +18,10 @@ import com.google.gwt.user.client.ui.*;
|
|||||||
import io.anuke.mindustry.Mindustry;
|
import io.anuke.mindustry.Mindustry;
|
||||||
import io.anuke.mindustry.io.Platform;
|
import io.anuke.mindustry.io.Platform;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.ucore.core.Settings;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
public class HtmlLauncher extends GwtApplication {
|
public class HtmlLauncher extends GwtApplication {
|
||||||
static final int WIDTH = 800;
|
static final int WIDTH = 800;
|
||||||
@@ -112,6 +115,22 @@ public class HtmlLauncher extends GwtApplication {
|
|||||||
String ref = Document.get().getReferrer();
|
String ref = Document.get().getReferrer();
|
||||||
return !ref.startsWith("https") && !ref.contains("itch.io");
|
return !ref.startsWith("https") && !ref.contains("itch.io");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getUUID(){
|
||||||
|
Settings.defaults("uuid", "");
|
||||||
|
|
||||||
|
String uuid = Settings.getString("uuid");
|
||||||
|
if(uuid.isEmpty()){
|
||||||
|
byte[] result = new byte[8];
|
||||||
|
new Random().nextBytes(result);
|
||||||
|
uuid = new String(Base64Coder.encode(result));
|
||||||
|
Settings.putString("uuid", uuid);
|
||||||
|
Settings.save();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Base64Coder.decode(uuid);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Mindustry();
|
return new Mindustry();
|
||||||
|
|||||||
@@ -7,22 +7,18 @@ import com.badlogic.gdx.utils.reflect.ReflectionException;
|
|||||||
import com.sksamuel.gwt.websockets.Websocket;
|
import com.sksamuel.gwt.websockets.Websocket;
|
||||||
import com.sksamuel.gwt.websockets.WebsocketListener;
|
import com.sksamuel.gwt.websockets.WebsocketListener;
|
||||||
import io.anuke.mindustry.io.Platform;
|
import io.anuke.mindustry.io.Platform;
|
||||||
import io.anuke.mindustry.net.Host;
|
import io.anuke.mindustry.net.*;
|
||||||
import io.anuke.mindustry.net.Net;
|
|
||||||
import io.anuke.mindustry.net.Net.ClientProvider;
|
import io.anuke.mindustry.net.Net.ClientProvider;
|
||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
import io.anuke.mindustry.net.Packet;
|
|
||||||
import io.anuke.mindustry.net.Packets.Connect;
|
import io.anuke.mindustry.net.Packets.Connect;
|
||||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
import io.anuke.mindustry.net.Packets.Disconnect;
|
||||||
import io.anuke.mindustry.net.Registrator;
|
|
||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
import io.anuke.ucore.util.Strings;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.webPort;
|
||||||
|
|
||||||
public class WebsocketClient implements ClientProvider {
|
public class WebsocketClient implements ClientProvider {
|
||||||
Websocket socket;
|
Websocket socket;
|
||||||
@@ -104,39 +100,45 @@ public class WebsocketClient implements ClientProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public 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) {
|
||||||
if(!Platform.instance.canJoinGame()) {
|
try {
|
||||||
failed.accept(new IOException());
|
if (!Platform.instance.canJoinGame()) {
|
||||||
}else {
|
failed.accept(new IOException());
|
||||||
Websocket socket = new Websocket("ws://" + address + ":" + webPort);
|
} else {
|
||||||
final boolean[] accepted = {false};
|
Websocket socket = new Websocket("ws://" + address + ":" + webPort);
|
||||||
socket.addListener(new WebsocketListener() {
|
final boolean[] accepted = {false};
|
||||||
@Override
|
socket.addListener(new WebsocketListener() {
|
||||||
public void onClose() {
|
@Override
|
||||||
if (!accepted[0]) failed.accept(new IOException("Failed to connect to host."));
|
public void onClose() {
|
||||||
}
|
if (!accepted[0]) failed.accept(new IOException("Failed to connect to host."));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(String msg) {
|
public void onMessage(String msg) {
|
||||||
if(!msg.startsWith("---")) return;
|
byte[] bytes = Base64Coder.decode(msg);
|
||||||
String[] text = msg.substring(3).split("\\|");
|
Host host = NetworkIO.readServerData(address, ByteBuffer.wrap(bytes));
|
||||||
Host host = new Host(text[1], address, text[2], Strings.parseInt(text[3]), Strings.parseInt(text[0]));
|
if(bytes.length != 128)
|
||||||
valid.accept(host);
|
valid.accept(new Host("Unknown", address, "Unknown", 0, 0, 0));
|
||||||
accepted[0] = true;
|
else
|
||||||
socket.close();
|
valid.accept(host);
|
||||||
}
|
accepted[0] = true;
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen() {
|
public void onOpen() {
|
||||||
socket.send("_ping_");
|
socket.send("ping");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.open();
|
socket.open();
|
||||||
Timers.runTask(60f * 5, () -> {
|
Timers.runTask(60f * 5, () -> {
|
||||||
if (!accepted[0]) {
|
if (!accepted[0]) {
|
||||||
failed.accept(new IOException("Failed to connect to host."));
|
failed.accept(new IOException("Failed to connect to host."));
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
failed.accept(new IOException("Failed to connect to host."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import io.anuke.mindustry.net.Host;
|
|||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.net.Net.ClientProvider;
|
import io.anuke.mindustry.net.Net.ClientProvider;
|
||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
|
import io.anuke.mindustry.net.NetworkIO;
|
||||||
import io.anuke.mindustry.net.Packets.Connect;
|
import io.anuke.mindustry.net.Packets.Connect;
|
||||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
import io.anuke.mindustry.net.Packets.Disconnect;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
@@ -41,7 +42,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
@Override
|
@Override
|
||||||
public void onDiscoveredHost(DatagramPacket datagramPacket) {
|
public void onDiscoveredHost(DatagramPacket datagramPacket) {
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
|
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
|
||||||
Host address = KryoRegistrator.readServerData(datagramPacket.getAddress(), buffer);
|
Host address = NetworkIO.readServerData(datagramPacket.getAddress().getHostAddress(), buffer);
|
||||||
addresses.put(datagramPacket.getAddress(), address);
|
addresses.put(datagramPacket.getAddress(), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +157,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
socket.receive(packet);
|
socket.receive(packet);
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||||
Host host = KryoRegistrator.readServerData(packet.getAddress(), buffer);
|
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
|
||||||
|
|
||||||
if (host != null) {
|
if (host != null) {
|
||||||
Gdx.app.postRunnable(() -> valid.accept(host));
|
Gdx.app.postRunnable(() -> valid.accept(host));
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ package io.anuke.kryonet;
|
|||||||
|
|
||||||
import com.esotericsoftware.minlog.Log;
|
import com.esotericsoftware.minlog.Log;
|
||||||
import com.esotericsoftware.minlog.Log.Logger;
|
import com.esotericsoftware.minlog.Log.Logger;
|
||||||
import io.anuke.mindustry.net.Host;
|
|
||||||
import io.anuke.ucore.util.ColorCodes;
|
import io.anuke.ucore.util.ColorCodes;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.headless;
|
||||||
|
|
||||||
public class KryoRegistrator {
|
public class KryoRegistrator {
|
||||||
public static boolean fakeLag = false;
|
public static boolean fakeLag = false;
|
||||||
@@ -45,46 +42,4 @@ public class KryoRegistrator {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteBuffer writeServerData(){
|
|
||||||
int maxlen = 32;
|
|
||||||
|
|
||||||
String host = (headless ? "Server" : player.name);
|
|
||||||
String map = world.getMap().name;
|
|
||||||
|
|
||||||
host = host.substring(0, Math.min(host.length(), maxlen));
|
|
||||||
map = map.substring(0, Math.min(map.length(), maxlen));
|
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(128);
|
|
||||||
|
|
||||||
buffer.put((byte)host.getBytes().length);
|
|
||||||
buffer.put(host.getBytes());
|
|
||||||
|
|
||||||
buffer.put((byte)map.getBytes().length);
|
|
||||||
buffer.put(map.getBytes());
|
|
||||||
|
|
||||||
buffer.putInt(playerGroup.size());
|
|
||||||
buffer.putInt(state.wave);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Host readServerData(InetAddress ia, ByteBuffer buffer){
|
|
||||||
if(buffer.capacity() < 128) return null; //old version address.
|
|
||||||
|
|
||||||
byte hlength = buffer.get();
|
|
||||||
byte[] hb = new byte[hlength];
|
|
||||||
buffer.get(hb);
|
|
||||||
|
|
||||||
byte mlength = buffer.get();
|
|
||||||
byte[] mb = new byte[mlength];
|
|
||||||
buffer.get(mb);
|
|
||||||
|
|
||||||
String host = new String(hb);
|
|
||||||
String map = new String(mb);
|
|
||||||
|
|
||||||
int players = buffer.getInt();
|
|
||||||
int wave = buffer.getInt();
|
|
||||||
|
|
||||||
return new Host(host, ia.getHostAddress(), map, wave, players);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,10 @@ import com.esotericsoftware.kryonet.Listener.LagListener;
|
|||||||
import com.esotericsoftware.kryonet.Server;
|
import com.esotericsoftware.kryonet.Server;
|
||||||
import com.esotericsoftware.kryonet.util.InputStreamSender;
|
import com.esotericsoftware.kryonet.util.InputStreamSender;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.*;
|
||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
import io.anuke.mindustry.net.Net.ServerProvider;
|
import io.anuke.mindustry.net.Net.ServerProvider;
|
||||||
import io.anuke.mindustry.net.NetConnection;
|
|
||||||
import io.anuke.mindustry.net.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import io.anuke.mindustry.net.Registrator;
|
|
||||||
import io.anuke.mindustry.net.Streamable;
|
|
||||||
import io.anuke.mindustry.net.Streamable.StreamBegin;
|
import io.anuke.mindustry.net.Streamable.StreamBegin;
|
||||||
import io.anuke.mindustry.net.Streamable.StreamChunk;
|
import io.anuke.mindustry.net.Streamable.StreamChunk;
|
||||||
import io.anuke.ucore.UCore;
|
import io.anuke.ucore.UCore;
|
||||||
@@ -37,11 +34,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.headless;
|
import static io.anuke.mindustry.Vars.headless;
|
||||||
import static io.anuke.mindustry.Vars.state;
|
|
||||||
import static io.anuke.mindustry.Vars.world;
|
|
||||||
|
|
||||||
public class KryoServer implements ServerProvider {
|
public class KryoServer implements ServerProvider {
|
||||||
final boolean debug = false;
|
|
||||||
final Server server;
|
final Server server;
|
||||||
final ByteSerializer serializer = new ByteSerializer();
|
final ByteSerializer serializer = new ByteSerializer();
|
||||||
final ByteBuffer buffer = ByteBuffer.allocate(4096);
|
final ByteBuffer buffer = ByteBuffer.allocate(4096);
|
||||||
@@ -56,7 +50,7 @@ public class KryoServer implements ServerProvider {
|
|||||||
public KryoServer(){
|
public KryoServer(){
|
||||||
server = new Server(4096*2, 2048, connection -> new ByteSerializer());
|
server = new Server(4096*2, 2048, connection -> new ByteSerializer());
|
||||||
server.setDiscoveryHandler((datagramChannel, fromAddress) -> {
|
server.setDiscoveryHandler((datagramChannel, fromAddress) -> {
|
||||||
ByteBuffer buffer = KryoRegistrator.writeServerData();
|
ByteBuffer buffer = NetworkIO.writeServerData();
|
||||||
buffer.position(0);
|
buffer.position(0);
|
||||||
datagramChannel.send(buffer, fromAddress);
|
datagramChannel.send(buffer, fromAddress);
|
||||||
return true;
|
return true;
|
||||||
@@ -183,26 +177,28 @@ public class KryoServer implements ServerProvider {
|
|||||||
connections.clear();
|
connections.clear();
|
||||||
lastconnection = 0;
|
lastconnection = 0;
|
||||||
|
|
||||||
Thread thread = new Thread(() -> {
|
async(server::close);
|
||||||
try {
|
|
||||||
server.close();
|
//kill them all
|
||||||
try {
|
for (Thread worker : Thread.getAllStackTraces().keySet()) {
|
||||||
if (webServer != null) webServer.stop(1);
|
if (worker.getName().contains("WebSocketWorker")) {
|
||||||
}catch(Exception e){
|
worker.interrupt();
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
//kill them all
|
|
||||||
for(Thread worker : Thread.getAllStackTraces().keySet()){
|
|
||||||
if(worker.getName().contains("WebSocketWorker")){
|
|
||||||
worker.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch (Exception e){
|
|
||||||
Gdx.app.postRunnable(() -> {throw new RuntimeException(e);});
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
try {
|
||||||
|
if (webServer != null) webServer.stop(1);
|
||||||
|
}catch (NullPointerException e){
|
||||||
|
try {
|
||||||
|
synchronized (webServer) {
|
||||||
|
((Thread) UCore.getPrivate(WebSocketServer.class, webServer, "selectorthread")).join(1);
|
||||||
|
}
|
||||||
|
}catch (InterruptedException j){
|
||||||
|
handleException(j);
|
||||||
|
}
|
||||||
|
}catch (InterruptedException e){
|
||||||
|
handleException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -290,25 +286,7 @@ public class KryoServer implements ServerProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(){
|
public void dispose(){
|
||||||
try {
|
close();
|
||||||
if(serverThread != null) serverThread.interrupt();
|
|
||||||
server.dispose();
|
|
||||||
}catch (Exception e){
|
|
||||||
Log.err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if(webServer != null) webServer.stop(1);
|
|
||||||
//kill them all
|
|
||||||
for(Thread thread : Thread.getAllStackTraces().keySet()){
|
|
||||||
if(thread.getName().contains("WebSocketWorker")){
|
|
||||||
thread.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch (Exception e){
|
|
||||||
Log.err(e);
|
|
||||||
}
|
|
||||||
Log.info("Disposed server.");
|
Log.info("Disposed server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,25 +316,10 @@ public class KryoServer implements ServerProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void throwErrorAndExit(){
|
void async(Runnable run){
|
||||||
Log.err("KRYONET CONNECTIONS:");
|
Thread thread = new Thread(run);
|
||||||
for(Connection c : server.getConnections()){
|
thread.setDaemon(true);
|
||||||
NetConnection k = getByKryoID(c.getID());
|
thread.start();
|
||||||
Log.err(" - Kryonet connection / ID {0} / IP {1} / NetConnection ID {2}",
|
|
||||||
c.getID(), c.getRemoteAddressTCP().getAddress().getHostAddress(), k == null ? "NOT FOUND" : k.id);
|
|
||||||
}
|
|
||||||
Log.err("NET CONNECTIONS:");
|
|
||||||
for(NetConnection c : connections){
|
|
||||||
Log.err(" - NetConnection / ID {0} / IP {1}", c.id, c.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.err("\nSTACK TRACE:");
|
|
||||||
|
|
||||||
StackTraceElement[] e = Thread.getAllStackTraces().get(Thread.currentThread());
|
|
||||||
for(StackTraceElement s : e){
|
|
||||||
Log.err("- {0}", s);
|
|
||||||
}
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class KryoConnection extends NetConnection{
|
class KryoConnection extends NetConnection{
|
||||||
@@ -381,14 +344,12 @@ public class KryoServer implements ServerProvider {
|
|||||||
try {
|
try {
|
||||||
synchronized (buffer) {
|
synchronized (buffer) {
|
||||||
buffer.position(0);
|
buffer.position(0);
|
||||||
if(debug) Log.info("Sending object with ID {0}", Registrator.getID(object.getClass()));
|
|
||||||
serializer.write(buffer, object);
|
serializer.write(buffer, object);
|
||||||
int pos = buffer.position();
|
int pos = buffer.position();
|
||||||
buffer.position(0);
|
buffer.position(0);
|
||||||
byte[] out = new byte[pos];
|
byte[] out = new byte[pos];
|
||||||
buffer.get(out);
|
buffer.get(out);
|
||||||
String string = new String(Base64Coder.encode(out));
|
String string = new String(Base64Coder.encode(out));
|
||||||
if(debug) Log.info("Sending string: {0}", string);
|
|
||||||
socket.send(string);
|
socket.send(string);
|
||||||
}
|
}
|
||||||
}catch (WebsocketNotConnectedException e){
|
}catch (WebsocketNotConnectedException e){
|
||||||
@@ -443,16 +404,7 @@ public class KryoServer implements ServerProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
public void onOpen(WebSocket conn, ClientHandshake handshake) {}
|
||||||
Connect connect = new Connect();
|
|
||||||
connect.addressTCP = conn.getRemoteSocketAddress().getAddress().getHostAddress();
|
|
||||||
KryoConnection kn = new KryoConnection(lastconnection ++, connect.addressTCP, conn);
|
|
||||||
|
|
||||||
Log.info("&bRecieved web connection: {0} {1}", kn.id, connect.addressTCP);
|
|
||||||
connections.add(kn);
|
|
||||||
|
|
||||||
Gdx.app.postRunnable(() -> Net.handleServerReceived(kn.id, connect));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
|
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
|
||||||
@@ -470,18 +422,31 @@ public class KryoServer implements ServerProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void onMessage(WebSocket conn, String message) {
|
public void onMessage(WebSocket conn, String message) {
|
||||||
try {
|
try {
|
||||||
KryoConnection k = getBySocket(conn);
|
if(message.equals("ping")){
|
||||||
if (k == null) return;
|
ByteBuffer ping = NetworkIO.writeServerData();
|
||||||
|
conn.send(new String(Base64Coder.encode(ping.array())));
|
||||||
if(message.equals("_ping_")){
|
|
||||||
conn.send("---" + Vars.playerGroup.size() + "|" + (headless ? "Server" : Vars.player.name)
|
|
||||||
+ "|" + world.getMap().name + "|" + state.wave);
|
|
||||||
connections.remove(k);
|
|
||||||
}else {
|
}else {
|
||||||
|
KryoConnection k = getBySocket(conn);
|
||||||
|
|
||||||
|
if (k == null){
|
||||||
|
Connect connect = new Connect();
|
||||||
|
connect.addressTCP = conn.getRemoteSocketAddress().getAddress().getHostAddress();
|
||||||
|
k = new KryoConnection(lastconnection ++, connect.addressTCP, conn);
|
||||||
|
|
||||||
|
Log.info("&bRecieved web connection: {0} {1}", k.id, connect.addressTCP);
|
||||||
|
connections.add(k);
|
||||||
|
|
||||||
|
int id = k.id;
|
||||||
|
|
||||||
|
Gdx.app.postRunnable(() -> Net.handleServerReceived(id, connect));
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = k.id;
|
||||||
|
|
||||||
byte[] out = Base64Coder.decode(message);
|
byte[] out = Base64Coder.decode(message);
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(out);
|
ByteBuffer buffer = ByteBuffer.wrap(out);
|
||||||
Object o = serializer.read(buffer);
|
Object o = serializer.read(buffer);
|
||||||
Gdx.app.postRunnable(() -> Net.handleServerReceived(k.id, o));
|
Gdx.app.postRunnable(() -> Net.handleServerReceived(id, o));
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import io.anuke.mindustry.game.Difficulty;
|
|||||||
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
||||||
import io.anuke.mindustry.game.GameMode;
|
import io.anuke.mindustry.game.GameMode;
|
||||||
import io.anuke.mindustry.io.SaveIO;
|
import io.anuke.mindustry.io.SaveIO;
|
||||||
|
import io.anuke.mindustry.io.Version;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.net.NetEvents;
|
import io.anuke.mindustry.net.NetEvents;
|
||||||
import io.anuke.mindustry.net.Packets.ChatPacket;
|
import io.anuke.mindustry.net.Packets.ChatPacket;
|
||||||
@@ -74,27 +75,24 @@ public class ServerControl extends Module {
|
|||||||
Events.on(GameOverEvent.class, () -> {
|
Events.on(GameOverEvent.class, () -> {
|
||||||
info("Game over!");
|
info("Game over!");
|
||||||
|
|
||||||
Timers.runTask(30f, () -> {
|
Timers.runTask(10f, () -> {
|
||||||
state.set(State.menu);
|
state.set(State.menu);
|
||||||
Net.closeServer();
|
Net.closeServer();
|
||||||
|
|
||||||
Timers.runTask(30f, () -> {
|
if (mode != ShuffleMode.off) {
|
||||||
|
Array<Map> maps = mode == ShuffleMode.both ? world.maps().getAllMaps() :
|
||||||
|
mode == ShuffleMode.normal ? world.maps().getDefaultMaps() : world.maps().getCustomMaps();
|
||||||
|
|
||||||
if (mode != ShuffleMode.off) {
|
Map previous = world.getMap();
|
||||||
Array<Map> maps = mode == ShuffleMode.both ? world.maps().getAllMaps() :
|
Map map = previous;
|
||||||
mode == ShuffleMode.normal ? world.maps().getDefaultMaps() : world.maps().getCustomMaps();
|
while (map == previous || !map.visible) map = maps.random();
|
||||||
|
|
||||||
Map previous = world.getMap();
|
info("Selected next map to be {0}.", map.name);
|
||||||
Map map = previous;
|
state.set(State.playing);
|
||||||
while (map == previous || !map.visible) map = maps.random();
|
logic.reset();
|
||||||
|
world.loadMap(map);
|
||||||
info("Selected next map to be {0}.", map.name);
|
host();
|
||||||
state.set(State.playing);
|
}
|
||||||
logic.reset();
|
|
||||||
world.loadMap(map);
|
|
||||||
host();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,6 +107,10 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handler.register("version", "Displays server version info.", arg -> {
|
||||||
|
info("&lmVersion: &lyMindustry {0} {1} / {2}", Version.code, Version.type, Version.buildName);
|
||||||
|
});
|
||||||
|
|
||||||
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();
|
||||||
@@ -252,7 +254,7 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("kick", "<username>", "Kick a person by name.", arg -> {
|
handler.register("kick", "<username...>", "Kick a person by name.", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Not hosting a game yet. Calm down.");
|
err("Not hosting a game yet. Calm down.");
|
||||||
return;
|
return;
|
||||||
@@ -275,7 +277,7 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("ban", "<username>", "Ban a person by name.", arg -> {
|
handler.register("ban", "<username...>", "Ban a person by name.", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Can't ban people by name with no players.");
|
err("Can't ban people by name with no players.");
|
||||||
return;
|
return;
|
||||||
@@ -292,29 +294,42 @@ public class ServerControl extends Module {
|
|||||||
|
|
||||||
if(target != null){
|
if(target != null){
|
||||||
String ip = Net.getConnection(target.clientid).address;
|
String ip = Net.getConnection(target.clientid).address;
|
||||||
netServer.admins.banPlayer(ip);
|
netServer.admins.banPlayerIP(ip);
|
||||||
|
netServer.admins.banPlayerID(netServer.admins.getTrace(ip).uuid);
|
||||||
Net.kickConnection(target.clientid, KickReason.banned);
|
Net.kickConnection(target.clientid, KickReason.banned);
|
||||||
info("Banned player by IP: {0}", ip);
|
info("Banned player by IP and ID: {0} / {1}", ip, netServer.admins.getTrace(ip).uuid);
|
||||||
}else{
|
}else{
|
||||||
info("Nobody with that name could be found.");
|
info("Nobody with that name could be found.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("bans", "List all banned IPs.", arg -> {
|
handler.register("bans", "List all banned IPs and IDs.", arg -> {
|
||||||
Array<String> bans = netServer.admins.getBanned();
|
Array<String> bans = netServer.admins.getBanned();
|
||||||
|
|
||||||
if(bans.size == 0){
|
if(bans.size == 0){
|
||||||
Log.info("No banned players have been found.");
|
Log.info("No IP-banned players have been found.");
|
||||||
}else{
|
}else{
|
||||||
Log.info("&lyBanned players:");
|
Log.info("&lyBanned players [IP]:");
|
||||||
for(String string : bans){
|
for(String string : bans){
|
||||||
Log.info(" &luy {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string));
|
Log.info(" &ly {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<String> idbans = netServer.admins.getBannedIDs();
|
||||||
|
|
||||||
|
if(idbans.size == 0){
|
||||||
|
Log.info("No ID-banned players have been found.");
|
||||||
|
}else{
|
||||||
|
Log.info("&lmBanned players [ID]:");
|
||||||
|
for(String string : idbans){
|
||||||
|
Log.info(" &lm '{0}' / Last known name: '{1}' / Last known IP: '{2}'", string,
|
||||||
|
netServer.admins.getLastName(netServer.admins.getLastIP(string)), netServer.admins.getLastIP(string));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("banip", "<ip>", "Ban a person by IP.", arg -> {
|
handler.register("banip", "<ip>", "Ban a person by IP.", arg -> {
|
||||||
if(netServer.admins.banPlayer(arg[0])) {
|
if(netServer.admins.banPlayerIP(arg[0])) {
|
||||||
info("Banned player by IP: {0}.", arg[0]);
|
info("Banned player by IP: {0}.", arg[0]);
|
||||||
|
|
||||||
for(Player player : playerGroup.all()){
|
for(Player player : playerGroup.all()){
|
||||||
@@ -328,15 +343,49 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("unbanip", "<ip>", "Unban a person by IP.", arg -> {
|
handler.register("banid", "<id>", "Ban a person by their unique ID.", arg -> {
|
||||||
if(netServer.admins.unbanPlayer(arg[0])) {
|
if(netServer.admins.banPlayerID(arg[0])) {
|
||||||
|
info("Banned player by ID: {0}.", arg[0]);
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(netServer.admins.getTrace(Net.getConnection(player.clientid).address).uuid.equals(arg[0])){
|
||||||
|
Net.kickConnection(player.clientid, KickReason.banned);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
err("That ID is already banned!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("unbanip", "<ip>", "Completely unban a person by IP.", arg -> {
|
||||||
|
if(netServer.admins.unbanPlayerIP(arg[0])) {
|
||||||
info("Unbanned player by IP: {0}.", arg[0]);
|
info("Unbanned player by IP: {0}.", arg[0]);
|
||||||
|
for(String s : netServer.admins.getBannedIDs()){
|
||||||
|
if(netServer.admins.getLastIP(s).equals(arg[0])){
|
||||||
|
netServer.admins.unbanPlayerID(s);
|
||||||
|
Log.info("Also unbanned UUID '{0}' as it corresponds to this IP.", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
err("That IP is not banned!");
|
err("That IP is not banned!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("admin", "<username>", "Make a user admin", arg -> {
|
handler.register("unbanid", "<id>", "Completely unban a person by ID.", arg -> {
|
||||||
|
if(netServer.admins.unbanPlayerID(arg[0])) {
|
||||||
|
info("&lmUnbanned player by ID: {0}.", arg[0]);
|
||||||
|
String ip = netServer.admins.getLastIP(arg[0]);
|
||||||
|
if(!ip.equals("unknown")) {
|
||||||
|
netServer.admins.unbanPlayerIP(ip);
|
||||||
|
Log.info("Also unbanned IP '{0}' as it corresponds to this ID.", ip);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
err("That IP is not banned!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("admin", "<username...>", "Make a user admin", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Open the server first.");
|
err("Open the server first.");
|
||||||
return;
|
return;
|
||||||
@@ -361,7 +410,7 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("unadmin", "<username>", "Removes admin status from a player", arg -> {
|
handler.register("unadmin", "<username...>", "Removes admin status from a player", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Open the server first.");
|
err("Open the server first.");
|
||||||
return;
|
return;
|
||||||
@@ -449,6 +498,11 @@ public class ServerControl extends Module {
|
|||||||
});
|
});
|
||||||
|
|
||||||
handler.register("gameover", "Force a game over.", arg -> {
|
handler.register("gameover", "Force a game over.", arg -> {
|
||||||
|
if(state.is(State.menu)){
|
||||||
|
info("Not playing a map.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
world.removeBlock(world.getCore());
|
world.removeBlock(world.getCore());
|
||||||
info("Core destroyed.");
|
info("Core destroyed.");
|
||||||
});
|
});
|
||||||
@@ -484,7 +538,7 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("trace", "<username>", "Trace a player's actions", arg -> {
|
handler.register("trace", "<username...>", "Trace a player's actions", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Open the server first.");
|
err("Open the server first.");
|
||||||
return;
|
return;
|
||||||
@@ -504,7 +558,9 @@ public class ServerControl extends Module {
|
|||||||
Log.info("&lcTrace info for player '{0}':", target.name);
|
Log.info("&lcTrace info for player '{0}':", target.name);
|
||||||
Log.info(" &lyEntity ID: {0}", info. playerid);
|
Log.info(" &lyEntity ID: {0}", info. playerid);
|
||||||
Log.info(" &lyIP: {0}", info.ip);
|
Log.info(" &lyIP: {0}", info.ip);
|
||||||
|
Log.info(" &lyUUID: {0}", info.uuid);
|
||||||
Log.info(" &lycustom client: {0}", info.modclient);
|
Log.info(" &lycustom client: {0}", info.modclient);
|
||||||
|
Log.info(" &lyandroid: {0}", info.android);
|
||||||
Log.info("");
|
Log.info("");
|
||||||
Log.info(" &lytotal blocks broken: {0}", info.totalBlocksBroken);
|
Log.info(" &lytotal blocks broken: {0}", info.totalBlocksBroken);
|
||||||
Log.info(" &lystructure blocks broken: {0}", info.structureBlocksBroken);
|
Log.info(" &lystructure blocks broken: {0}", info.structureBlocksBroken);
|
||||||
|
|||||||
Reference in New Issue
Block a user