Merge
|
|
@ -86,10 +86,9 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||
|
||||
//load.addStatement(name + " = io.anuke.arc.Core.audio."+loadMethod+"(io.anuke.arc.Core.files.internal(io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? $S : $S))",
|
||||
//filepath, filepath.replace(".ogg", ".mp3"));
|
||||
String filename = "io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? \"" + filepath + "\" : \"" + filepath.replace(".ogg", ".mp3")+"\"";
|
||||
|
||||
loadBegin.addStatement("io.anuke.arc.Core.assets.load(io.anuke.arc.Core.app.getType() != io.anuke.arc.Application.ApplicationType.iOS ? $S : $S, "+rtype+".class, " +
|
||||
"new io.anuke.arc.assets.loaders."+classname.substring(0, classname.length()-1)+"Loader."+classname.substring(0, classname.length()-1)+"Parameter((m, name, type) -> " + name + " = m.get(\"" + filepath + "\")))",
|
||||
filepath, filepath.replace(".ogg", ".mp3"));
|
||||
loadBegin.addStatement("io.anuke.arc.Core.assets.load("+filename +", "+rtype+".class).loaded = a -> " + name + " = ("+rtype+")a", filepath, filepath.replace(".ogg", ".mp3"));
|
||||
|
||||
|
||||
dispose.addStatement(name + ".dispose()");
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ project(":ios"){
|
|||
}
|
||||
|
||||
props['app.id'] = 'io.anuke.mindustry'
|
||||
props['app.version'] = '4.0'
|
||||
props['app.version'] = '4.2.1'
|
||||
props['app.mainclass'] = 'io.anuke.mindustry.IOSLauncher'
|
||||
props['app.executable'] = 'IOSLauncher'
|
||||
props['app.name'] = 'Mindustry'
|
||||
|
|
@ -205,6 +205,7 @@ project(":core"){
|
|||
apply plugin: "java"
|
||||
|
||||
task preGen{
|
||||
outputs.upToDateWhen{ false }
|
||||
generateLocales()
|
||||
writeVersion()
|
||||
}
|
||||
|
|
@ -325,4 +326,4 @@ task deployAll{
|
|||
dependsOn "desktop:packrMacOS"
|
||||
dependsOn "server:deploy"
|
||||
dependsOn "android:deploy"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
core/assets-raw/sprites/ui/icons/icon-fdroid.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -64,16 +64,18 @@ techtree = Tech Tree
|
|||
research.list = [lightgray]Research:
|
||||
research = Research
|
||||
researched = [lightgray]{0} researched.
|
||||
players = {0} players online
|
||||
players.single = {0} player online
|
||||
players = {0} players
|
||||
players.single = {0} player
|
||||
server.closing = [accent]Closing server...
|
||||
server.kicked.kick = You have been kicked from the server!
|
||||
server.kicked.whitelist = You are not whitelisted here.
|
||||
server.kicked.serverClose = Server closed.
|
||||
server.kicked.vote = You have been vote-kicked. Goodbye.
|
||||
server.kicked.clientOutdated = Outdated client! Update your game!
|
||||
server.kicked.serverOutdated = Outdated server! Ask the host to update!
|
||||
server.kicked.banned = You are banned on this server.
|
||||
server.kicked.typeMismatch = This server is not compatible with your build type.
|
||||
server.kicked.playerLimit = This server is full. Wait for an empty slot.
|
||||
server.kicked.recentKick = You have been kicked recently.\nWait before connecting again.
|
||||
server.kicked.nameInUse = There is someone with that name\nalready on this server.
|
||||
server.kicked.nameEmpty = Your chosen name is invalid.
|
||||
|
|
@ -110,7 +112,7 @@ server.edit = Edit Server
|
|||
server.outdated = [crimson]Outdated Server![]
|
||||
server.outdated.client = [crimson]Outdated Client![]
|
||||
server.version = [gray]v{0} {1}
|
||||
server.custombuild = [yellow]Custom Build
|
||||
server.custombuild = [accent]Custom Build
|
||||
confirmban = Are you sure you want to ban this player?
|
||||
confirmkick = Are you sure you want to kick this player?
|
||||
confirmunban = Are you sure you want to unban this player?
|
||||
|
|
@ -119,6 +121,9 @@ confirmunadmin = Are you sure you want to remove admin status from this player?
|
|||
joingame.title = Join Game
|
||||
joingame.ip = Address:
|
||||
disconnect = Disconnected.
|
||||
disconnect.error = Connection error.
|
||||
disconnect.closed = Connection closed.
|
||||
disconnect.timeout = Timed out.
|
||||
disconnect.data = Failed to load world data!
|
||||
connecting = [accent]Connecting...
|
||||
connecting.data = [accent]Loading world data...
|
||||
|
|
@ -152,7 +157,7 @@ off = Off
|
|||
save.autosave = Autosave: {0}
|
||||
save.map = Map: {0}
|
||||
save.wave = Wave {0}
|
||||
save.difficulty = Difficulty: {0}
|
||||
save.mode = Gamemode: {0}
|
||||
save.date = Last Saved: {0}
|
||||
save.playtime = Playtime: {0}
|
||||
warning = Warning.
|
||||
|
|
@ -446,6 +451,7 @@ blocks.boosteffect = Boost Effect
|
|||
blocks.maxunits = Max Active Units
|
||||
blocks.health = Health
|
||||
blocks.buildtime = Build Time
|
||||
blocks.buildcost = Build Cost
|
||||
blocks.inaccuracy = Inaccuracy
|
||||
blocks.shots = Shots
|
||||
blocks.reload = Shots/Second
|
||||
|
|
@ -890,8 +896,8 @@ tutorial.intro = You have entered the[scarlet] Mindustry Tutorial.[]\nBegin by[a
|
|||
tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nClick the drill tab in the bottom right.\nSelect the[accent] mechanical drill[]. Place it on a copper vein by clicking.\n[accent]Right-click[] to stop building.
|
||||
tutorial.drill.mobile = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nTap the drill tab in the bottom right.\nSelect the[accent] mechanical drill[].\nPlace it on a copper vein by tapping, then press the[accent] checkmark[] below to confirm your selection.\nPress the[accent] X button[] to cancel placement.
|
||||
tutorial.blockinfo = Each block has different stats. Each drill can only mine certain ores.\nTo check a block's info and stats,[accent] tap the "?" button while selecting it in the build menu.[]\n\n[accent]Access the Mechanical Drill's stats now.[]
|
||||
tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent]Hold down the mouse to place in a line.[]\nHold[accent] CTRL[] while selecting a line to place diagonally.\n\n[accent]{0}/{1} conveyors placed in line\n[accent]0/1 items delivered
|
||||
tutorial.conveyor.mobile = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent] Place in a line by holding down your finger for a few seconds[] and dragging in a direction.\n\n[accent]{0}/{1} conveyors placed in line\n[accent]0/1 items delivered
|
||||
tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent]Hold down the mouse to place in a line.[]\nHold[accent] CTRL[] while selecting a line to place diagonally.\n\n[accent]Place 2 conveyors with the line tool, then deliver an item into the core.
|
||||
tutorial.conveyor.mobile = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent] Place in a line by holding down your finger for a few seconds[] and dragging in a direction.\n\n[accent]Place 2 conveyors with the line tool, then deliver an item into the core.
|
||||
tutorial.turret = Once an item enters your core, it can be used for building.\nKeep in mind that not all items can be used for building.\nItems that are not used for building, such as[accent] coal[] or[accent] scrap[], cannot be put into the core.\nDefensive structures must be built to repel the[lightgray] enemy[].\nBuild a[accent] duo turret[] near your base.
|
||||
tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill near the turret.\nLead conveyors into the turret to supply it with copper.\n\n[accent]Ammo delivered: 0/1
|
||||
tutorial.pause = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press space to pause.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 718 B |
|
Before Width: | Height: | Size: 682 KiB After Width: | Height: | Size: 672 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 579 KiB After Width: | Height: | Size: 566 KiB |
|
|
@ -84,10 +84,12 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
|||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
super.resize(width, height);
|
||||
if(assets == null) return;
|
||||
|
||||
if(!assets.isFinished()){
|
||||
Draw.proj().setOrtho(0, 0, width, height);
|
||||
}else{
|
||||
super.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +102,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
|||
for(ApplicationListener listener : modules){
|
||||
listener.init();
|
||||
}
|
||||
super.resize(graphics.getWidth(), graphics.getHeight());
|
||||
finished = true;
|
||||
Events.fire(new ClientLoadEvent());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package io.anuke.mindustry;
|
|||
import io.anuke.arc.Application.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
|
|
@ -43,6 +44,8 @@ public class Vars implements Loadable{
|
|||
public static final String discordURL = "https://discord.gg/mindustry";
|
||||
/** URL for sending crash reports to */
|
||||
public static final String crashReportURL = "http://mins.us.to/report";
|
||||
/** list of built-in servers.*/
|
||||
public static final Array<String> defaultServers = Array.with(/*"mins.us.to"*/);
|
||||
/** maximum distance between mine and core that supports automatic transferring */
|
||||
public static final float mineTransferRange = 220f;
|
||||
/** team of the player by default */
|
||||
|
|
|
|||
|
|
@ -242,14 +242,13 @@ public class BlockIndexer{
|
|||
int quadrantY = tile.y / quadrantSize;
|
||||
itemSet.clear();
|
||||
|
||||
Tile rounded = world.tile(Mathf.clamp(quadrantX * quadrantSize + quadrantSize / 2, 0, world.width() - 1),
|
||||
Mathf.clamp(quadrantY * quadrantSize + quadrantSize / 2, 0, world.height() - 1));
|
||||
Tile rounded = world.tile(Mathf.clamp(quadrantX * quadrantSize + quadrantSize / 2, 0, world.width() - 1), Mathf.clamp(quadrantY * quadrantSize + quadrantSize / 2, 0, world.height() - 1));
|
||||
|
||||
//find all items that this quadrant contains
|
||||
for(int x = quadrantX * quadrantSize; x < world.width() && x < (quadrantX + 1) * quadrantSize; x++){
|
||||
for(int y = quadrantY * quadrantSize; y < world.height() && y < (quadrantY + 1) * quadrantSize; y++){
|
||||
for(int x = Math.max(0, rounded.x - quadrantSize / 2); x < rounded.x + quadrantSize / 2 && x < world.width(); x++){
|
||||
for(int y = Math.max(0, rounded.y - quadrantSize / 2); y < rounded.y + quadrantSize / 2 && y < world.height(); y++){
|
||||
Tile result = world.tile(x, y);
|
||||
if(result == null || result.drop() == null || !scanOres.contains(result.drop())) continue;
|
||||
if(result == null || result.drop() == null || !scanOres.contains(result.drop()) || result.block() != Blocks.air) continue;
|
||||
|
||||
itemSet.add(result.drop());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -500,6 +500,7 @@ public class Blocks implements ContentList{
|
|||
|
||||
consumes.items(new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10));
|
||||
consumes.power(5f);
|
||||
itemCapacity = 20;
|
||||
|
||||
int bottomRegion = reg("-bottom"), weaveRegion = reg("-weave");
|
||||
|
||||
|
|
@ -982,7 +983,6 @@ public class Blocks implements ContentList{
|
|||
pulseConduit = new Conduit("pulse-conduit"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 1, Items.metaglass, 1));
|
||||
liquidCapacity = 16f;
|
||||
liquidFlowFactor = 4.9f;
|
||||
health = 90;
|
||||
}};
|
||||
|
||||
|
|
@ -1405,14 +1405,14 @@ public class Blocks implements ContentList{
|
|||
}};
|
||||
|
||||
arc = new PowerTurret("arc"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35, Items.lead, 35));
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35, Items.lead, 50));
|
||||
shootType = Bullets.arc;
|
||||
reload = 24f;
|
||||
reload = 35f;
|
||||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
powerUse = 0.9f;
|
||||
powerUse = 1.5f;
|
||||
targetAir = false;
|
||||
range = 95f;
|
||||
range = 90f;
|
||||
shootEffect = Fx.lightningShoot;
|
||||
heatColor = Color.RED;
|
||||
recoil = 1f;
|
||||
|
|
@ -1775,4 +1775,4 @@ public class Blocks implements ContentList{
|
|||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ import io.anuke.arc.util.*;
|
|||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Bullets implements ContentList{
|
||||
public static BulletType
|
||||
|
|
@ -631,7 +632,7 @@ public class Bullets implements ContentList{
|
|||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Lightning.create(b.getTeam(), Pal.lancerLaser, damage, b.x, b.y, b.rot(), 30);
|
||||
Lightning.create(b.getTeam(), Pal.lancerLaser, damage * (b.getOwner() instanceof Player ? state.rules.playerDamageMultiplier : 1f), b.x, b.y, b.rot(), 30);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ public class Control implements ApplicationListener, Loadable{
|
|||
private boolean wasPaused = false;
|
||||
|
||||
public Control(){
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
Events.on(StateChangeEvent.class, event -> {
|
||||
if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){
|
||||
Time.runTask(5f, platform::updateRPC);
|
||||
|
|
@ -152,10 +156,6 @@ public class Control implements ApplicationListener, Loadable{
|
|||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth();
|
||||
|
||||
Core.input.setCatch(KeyCode.BACK, true);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,17 @@ public class NetClient implements ApplicationListener{
|
|||
|
||||
Time.runTask(3f, ui.loadfrag::hide);
|
||||
|
||||
ui.showError("$disconnect");
|
||||
if(packet.reason != null){
|
||||
if(packet.reason.equals("closed")){
|
||||
ui.showSmall("$disconnect", "$disconnect.closed");
|
||||
}else if(packet.reason.equals("timeout")){
|
||||
ui.showSmall("$disconnect", "$disconnect.timeout");
|
||||
}else if(packet.reason.equals("error")){
|
||||
ui.showSmall("$disconnect", "$disconnect.error");
|
||||
}
|
||||
}else{
|
||||
ui.showError("$disconnect");
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleClient(WorldStream.class, data -> {
|
||||
|
|
|
|||
|
|
@ -1,39 +1,32 @@
|
|||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.ApplicationListener;
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Colors;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.CommandHandler.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
import io.anuke.mindustry.entities.traits.SyncTrait;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.gen.RemoteReadServer;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Administration.PlayerInfo;
|
||||
import io.anuke.mindustry.net.Administration.TraceInfo;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.nio.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
|
|
@ -76,7 +69,7 @@ public class NetServer implements ApplicationListener{
|
|||
Net.handleServer(Disconnect.class, (id, packet) -> {
|
||||
Player player = connections.get(id);
|
||||
if(player != null){
|
||||
onDisconnect(player);
|
||||
onDisconnect(player, packet.reason);
|
||||
}
|
||||
connections.remove(id);
|
||||
});
|
||||
|
|
@ -111,6 +104,27 @@ public class NetServer implements ApplicationListener{
|
|||
return;
|
||||
}
|
||||
|
||||
if(admins.isIDBanned(uuid)){
|
||||
kick(id, KickReason.banned);
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
|
||||
kick(id, KickReason.playerLimit);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!admins.isWhitelisted(packet.uuid, packet.usid)){
|
||||
info.adminUsid = packet.usid;
|
||||
info.lastName = packet.name;
|
||||
info.id = packet.uuid;
|
||||
admins.save();
|
||||
Call.onInfoMessage(id, "You are not whitelisted here.");
|
||||
Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name);
|
||||
kick(id, KickReason.whitelist);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
|
|
@ -184,6 +198,8 @@ public class NetServer implements ApplicationListener{
|
|||
sendWorldData(player, id);
|
||||
|
||||
platform.updateRPC();
|
||||
|
||||
Events.fire(new PlayerJoin(player));
|
||||
});
|
||||
|
||||
Net.handleServer(InvokePacket.class, (id, packet) -> {
|
||||
|
|
@ -227,25 +243,34 @@ public class NetServer implements ApplicationListener{
|
|||
});
|
||||
|
||||
//duration of a a kick in seconds
|
||||
int kickDuration = 10 * 60;
|
||||
int kickDuration = 15 * 60;
|
||||
|
||||
class VoteSession{
|
||||
Player target;
|
||||
ObjectSet<String> voted = new ObjectSet<>();
|
||||
ObjectMap<Player, VoteSession> map;
|
||||
VoteSession[] map;
|
||||
Timer.Task task;
|
||||
int votes;
|
||||
|
||||
public VoteSession(ObjectMap<Player, VoteSession> map, Player target){
|
||||
public VoteSession(VoteSession[] map, Player target){
|
||||
this.target = target;
|
||||
this.map = map;
|
||||
this.task = Timer.schedule(() -> {
|
||||
if(!checkPass()){
|
||||
Call.sendMessage(Strings.format("[lightgray]Vote failed. Not enough votes to kick[orange] {0}[lightgray].", target.name));
|
||||
map[0] = null;
|
||||
task.cancel();
|
||||
}
|
||||
map.remove(target);
|
||||
task.cancel();
|
||||
}, 60 * 1.5f);
|
||||
}, 60 * 1);
|
||||
}
|
||||
|
||||
void vote(Player player, int d){
|
||||
votes += d;
|
||||
voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
|
||||
player.name, target.name, votes, votesRequired()));
|
||||
//checkPass();
|
||||
}
|
||||
|
||||
boolean checkPass(){
|
||||
|
|
@ -253,6 +278,8 @@ public class NetServer implements ApplicationListener{
|
|||
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;
|
||||
kick(target.con.id, KickReason.vote);
|
||||
map[0] = null;
|
||||
task.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -260,10 +287,10 @@ public class NetServer implements ApplicationListener{
|
|||
}
|
||||
|
||||
//cooldown between votes
|
||||
int voteTime = 60 * 10;
|
||||
int voteTime = 60 * 5;
|
||||
Timekeeper vtime = new Timekeeper(voteTime);
|
||||
//current kick sessions
|
||||
ObjectMap<Player, VoteSession> currentlyKicking = new ObjectMap<>();
|
||||
VoteSession[] currentlyKicking = {null};
|
||||
|
||||
clientCommands.<Player>register("votekick", "[player...]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
if(playerGroup.size() < 3){
|
||||
|
|
@ -271,8 +298,8 @@ public class NetServer implements ApplicationListener{
|
|||
return;
|
||||
}
|
||||
|
||||
if(currentlyKicking.values().toArray().contains(v -> v.voted.contains(player.uuid) || v.voted.contains(admins.getInfo(player.uuid).lastIP))){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
if(player.isLocal){
|
||||
player.sendMessage("[scarlet]Just kick them yourself if you're the host.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -295,24 +322,20 @@ public class NetServer implements ApplicationListener{
|
|||
}
|
||||
|
||||
if(found != null){
|
||||
if(player == found){
|
||||
player.sendMessage("[scarlet]If you're interested in kicking yourself, just leave.");
|
||||
}else if(found.isAdmin){
|
||||
if(found.isAdmin){
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else if(found.isLocal){
|
||||
player.sendMessage("[scarlet]Local players cannot be kicked.");
|
||||
}else{
|
||||
if(!currentlyKicking.containsKey(found) && !vtime.get()){
|
||||
if(!vtime.get()){
|
||||
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");
|
||||
return;
|
||||
}
|
||||
|
||||
VoteSession session = currentlyKicking.getOr(found, () -> new VoteSession(currentlyKicking, found));
|
||||
session.votes ++;
|
||||
session.voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /votekick #{4}[] to agree.",
|
||||
player.name, found.name, session.votes, votesRequired(), found.con.id));
|
||||
session.checkPass();
|
||||
vtime.reset();
|
||||
VoteSession session = new VoteSession(currentlyKicking, found);
|
||||
session.vote(player, 1);
|
||||
vtime.reset();
|
||||
currentlyKicking[0] = session;
|
||||
}
|
||||
}else{
|
||||
player.sendMessage("[scarlet]No player[orange]'" + args[0] + "'[scarlet] found.");
|
||||
|
|
@ -320,6 +343,31 @@ public class NetServer implements ApplicationListener{
|
|||
}
|
||||
});
|
||||
|
||||
clientCommands.<Player>register("vote", "<y/n>", "Vote to kick the current player.", (arg, player) -> {
|
||||
if(currentlyKicking[0] == null){
|
||||
player.sendMessage("[scarlet]Nobody is being voted on.");
|
||||
}else{
|
||||
if(currentlyKicking[0].voted.contains(player.uuid) || currentlyKicking[0].voted.contains(admins.getInfo(player.uuid).lastIP)){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentlyKicking[0].target == player){
|
||||
player.sendMessage("[scarlet]You can't vote on your own trial.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!arg[0].toLowerCase().equals("y") && !arg[0].toLowerCase().equals("n")){
|
||||
player.sendMessage("[scarlet]Vote either 'y' (yes) or 'n' (no).");
|
||||
return;
|
||||
}
|
||||
|
||||
int sign = arg[0].toLowerCase().equals("y") ? 1 : -1;
|
||||
currentlyKicking[0].vote(player, sign);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
clientCommands.<Player>register("sync", "Re-synchronize world state.", (args, player) -> {
|
||||
if(player.isLocal){
|
||||
player.sendMessage("[scarlet]Re-synchronizing as the host is pointless.");
|
||||
|
|
@ -331,7 +379,7 @@ public class NetServer implements ApplicationListener{
|
|||
}
|
||||
|
||||
public int votesRequired(){
|
||||
return playerGroup.size() * 2 / 3;
|
||||
return 2 + (playerGroup.size() > 4 ? 1 : 0);
|
||||
}
|
||||
|
||||
public Team assignTeam(Player current, Iterable<Player> players){
|
||||
|
|
@ -361,7 +409,7 @@ public class NetServer implements ApplicationListener{
|
|||
Log.debug("Packed {0} compressed bytes of world data.", stream.size());
|
||||
}
|
||||
|
||||
public static void onDisconnect(Player player){
|
||||
public static void onDisconnect(Player player, String reason){
|
||||
//singleplayer multiplayer wierdness
|
||||
if(player.con == null){
|
||||
player.remove();
|
||||
|
|
@ -369,12 +417,13 @@ public class NetServer implements ApplicationListener{
|
|||
}
|
||||
|
||||
if(player.con.hasConnected){
|
||||
Events.fire(new PlayerLeave(player));
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has disconnected.");
|
||||
Call.onPlayerDisconnect(player.id);
|
||||
}
|
||||
player.remove();
|
||||
netServer.connections.remove(player.con.id);
|
||||
Log.info("&lm[{1}] &lc{0} has disconnected.", player.name, player.uuid);
|
||||
Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
|
||||
}
|
||||
|
||||
private static float compound(float speed, float drag){
|
||||
|
|
@ -700,7 +749,7 @@ public class NetServer implements ApplicationListener{
|
|||
|
||||
if(connection == null || !connection.isConnected() || !connections.containsKey(connection.id)){
|
||||
//player disconnected, call d/c event
|
||||
onDisconnect(player);
|
||||
onDisconnect(player, "disappeared");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -360,6 +360,15 @@ public class UI implements ApplicationListener, Loadable{
|
|||
}}.show();
|
||||
}
|
||||
|
||||
public void showSmall(String titleText, String text){
|
||||
new Dialog(titleText, "dialog"){{
|
||||
cont.margin(10).add(text);
|
||||
titleTable.row();
|
||||
titleTable.addImage("whiteui").color(Pal.accent).height(3f).growX().pad(2f);
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showConfirm(String title, String text, Runnable confirmed){
|
||||
showConfirm(title, text, null, confirmed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||
|
||||
clearChildren();
|
||||
margin(0);
|
||||
shown(this::build);
|
||||
|
||||
update(() -> {
|
||||
if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){
|
||||
|
|
@ -228,6 +227,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||
platform.updateRPC();
|
||||
if(!Core.settings.getBool("landscape")) platform.endForceLandscape();
|
||||
});
|
||||
|
||||
shown(this::build);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.arc.math.geom.Position;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public interface SpawnerTrait extends TargetTrait, Position{
|
||||
|
|
@ -9,6 +9,8 @@ public interface SpawnerTrait extends TargetTrait, Position{
|
|||
|
||||
void updateSpawning(Player unit);
|
||||
|
||||
boolean hasUnit(Unit unit);
|
||||
|
||||
@Override
|
||||
default boolean isValid(){
|
||||
return getTile().entity instanceof SpawnerTrait;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
|||
//visual only.
|
||||
if(Net.client()){
|
||||
Tile tile = world.tile(unit.spawner);
|
||||
if(tile != null && !Net.client()){
|
||||
if(tile != null){
|
||||
tile.block().unitRemoved(tile, unit);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ public abstract class FlyingUnit extends BaseUnit{
|
|||
|
||||
if(!Net.client()){
|
||||
updateRotation();
|
||||
wobble();
|
||||
}
|
||||
wobble();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import io.anuke.mindustry.graphics.Pal;
|
|||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.input.InputHandler.PlaceDraw;
|
||||
import io.anuke.mindustry.io.TypeIO;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
|
@ -37,6 +38,7 @@ import static io.anuke.mindustry.Vars.*;
|
|||
public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public static final int timerSync = 2;
|
||||
public static final int timerAbility = 3;
|
||||
public static final int timerTransfer = 4;
|
||||
private static final int timerShootLeft = 0;
|
||||
private static final int timerShootRight = 1;
|
||||
private static final float liftoffBoost = 0.2f;
|
||||
|
|
@ -59,7 +61,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||
|
||||
public NetConnection con;
|
||||
public boolean isLocal = false;
|
||||
public Interval timer = new Interval(4);
|
||||
public Interval timer = new Interval(6);
|
||||
public TargetTrait target;
|
||||
public TargetTrait moveTarget;
|
||||
|
||||
|
|
@ -800,6 +802,14 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||
}
|
||||
}
|
||||
|
||||
public PlayerInfo getInfo(){
|
||||
if(uuid == null){
|
||||
throw new IllegalArgumentException("Local players cannot be traced and do not have info.");
|
||||
}else{
|
||||
return netServer.admins.getInfo(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets all values of the player. */
|
||||
public void reset(){
|
||||
resetNoAdd();
|
||||
|
|
@ -909,7 +919,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
|||
buffer.writeInt(Color.rgba8888(color));
|
||||
buffer.writeByte(mech.id);
|
||||
buffer.writeInt(mining == null ? noSpawner : mining.pos());
|
||||
buffer.writeInt(spawner == null ? noSpawner : spawner.getTile().pos());
|
||||
buffer.writeInt(spawner == null || !spawner.hasUnit(this) ? noSpawner : spawner.getTile().pos());
|
||||
buffer.writeShort((short)(baseRotation * 2));
|
||||
|
||||
writeBuilding(buffer);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import io.anuke.mindustry.entities.traits.BuilderTrait;
|
|||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.type.Zone;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
|
||||
public class EventType{
|
||||
|
||||
|
|
@ -184,5 +185,22 @@ public class EventType{
|
|||
public static class ResizeEvent{
|
||||
|
||||
}
|
||||
|
||||
public static class PlayerJoin{
|
||||
public final Player player;
|
||||
|
||||
public PlayerJoin(Player player){
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerLeave{
|
||||
public final Player player;
|
||||
|
||||
public PlayerLeave(Player player){
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,20 @@ public enum Gamemode{
|
|||
this.validator = validator;
|
||||
}
|
||||
|
||||
public static Gamemode bestFit(Rules rules){
|
||||
if(rules.pvp){
|
||||
return pvp;
|
||||
}else if(rules.editor){
|
||||
return editor;
|
||||
}else if(rules.attackMode){
|
||||
return attack;
|
||||
}else if(rules.infiniteResources){
|
||||
return sandbox;
|
||||
}else{
|
||||
return survival;
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies this preset to this ruleset. */
|
||||
public Rules apply(Rules in){
|
||||
rules.accept(in);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import static io.anuke.mindustry.Vars.*;
|
|||
|
||||
/** Controls playback of multiple music tracks.*/
|
||||
public class MusicControl{
|
||||
private static final float finTime = 120f, foutTime = 120f, musicInterval = 60 * 60 * 3f, musicChance = 0.5f, musicWaveChance = 0.4f;
|
||||
private static final float finTime = 120f, foutTime = 120f, musicInterval = 60 * 60 * 3f, musicChance = 0.6f, musicWaveChance = 0.5f;
|
||||
|
||||
/** normal, ambient music, plays at any time */
|
||||
public final Array<Music> ambientMusic = Array.with(Musics.game1, Musics.game3, Musics.game4, Musics.game6);
|
||||
|
|
|
|||
|
|
@ -266,6 +266,10 @@ public class Saves{
|
|||
return meta == null || meta.rules == null ? null : meta.rules.zone;
|
||||
}
|
||||
|
||||
public Gamemode mode(){
|
||||
return Gamemode.bestFit(meta.rules);
|
||||
}
|
||||
|
||||
public int getBuild(){
|
||||
return meta.build;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,9 +115,7 @@ public class Tutorial{
|
|||
outline("blockinfo");
|
||||
}
|
||||
},
|
||||
conveyor(
|
||||
line -> Strings.format(line, Math.min(placed(Blocks.conveyor), 2), 2),
|
||||
() -> placed(Blocks.conveyor, 2) && event("lineconfirm") && event("coreitem")){
|
||||
conveyor(() -> placed(Blocks.conveyor, 2) && event("lineconfirm") && event("coreitem")){
|
||||
void draw(){
|
||||
outline("category-distribution");
|
||||
outline("block-conveyor");
|
||||
|
|
@ -190,13 +188,12 @@ public class Tutorial{
|
|||
|
||||
protected final String line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
|
||||
protected final Function<String, String> text;
|
||||
protected final Array<String> sentences;
|
||||
protected Array<String> sentences;
|
||||
protected final BooleanProvider done;
|
||||
|
||||
TutorialStage(Function<String, String> text, BooleanProvider done){
|
||||
this.text = text;
|
||||
this.done = done;
|
||||
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
}
|
||||
|
||||
TutorialStage(BooleanProvider done){
|
||||
|
|
@ -205,6 +202,7 @@ public class Tutorial{
|
|||
|
||||
/** displayed tutorial stage text.*/
|
||||
public String text(){
|
||||
if(sentences == null) this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
String line = sentences.get(control.tutorial.sentence);
|
||||
return line.contains("{") ? text.get(line) : line;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public abstract class InputHandler implements InputProcessor{
|
|||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Tile tile){
|
||||
if(!player.timer.get(Player.timerTransfer, 40)) return;
|
||||
if(Net.server() && (player.item().amount <= 0 || player.isTransferring)){
|
||||
throw new ValidateException(player, "Player cannot transfer an item.");
|
||||
}
|
||||
|
|
@ -288,7 +289,7 @@ public abstract class InputHandler implements InputProcessor{
|
|||
}
|
||||
|
||||
public void tryDropItems(Tile tile, float x, float y){
|
||||
if(!droppingItem || player.item().amount <= 0 || canTapPlayer(x, y) || state.isPaused()){
|
||||
if(!droppingItem || player.item().amount <= 0 || canTapPlayer(x, y) || state.isPaused() || !player.timer.check(Player.timerTransfer, 40)){
|
||||
droppingItem = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ public class Maps{
|
|||
FileHandle dest = findFile();
|
||||
file.copyTo(dest);
|
||||
|
||||
loadMap(dest, true);
|
||||
createNewPreview(loadMap(dest, true));
|
||||
}
|
||||
|
||||
/** Attempts to run the following code;
|
||||
|
|
@ -356,7 +356,7 @@ public class Maps{
|
|||
//if it's here, then the preview failed to load or doesn't exist, make it
|
||||
//this has to be done synchronously!
|
||||
Pixmap pix = MapIO.generatePreview(map);
|
||||
Core.app.post(() -> map.texture = new Texture(pix));
|
||||
map.texture = new Texture(pix);
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
map.previewFile().writePNG(pix);
|
||||
|
|
@ -404,7 +404,7 @@ public class Maps{
|
|||
return customMapDirectory.child("map_" + i + "." + mapExtension);
|
||||
}
|
||||
|
||||
private void loadMap(FileHandle file, boolean custom) throws IOException{
|
||||
private Map loadMap(FileHandle file, boolean custom) throws IOException{
|
||||
Map map = MapIO.createMap(file, custom);
|
||||
|
||||
if(map.name() == null){
|
||||
|
|
@ -413,6 +413,7 @@ public class Maps{
|
|||
|
||||
maps.add(map);
|
||||
maps.sort();
|
||||
return map;
|
||||
}
|
||||
|
||||
private void loadCustomMaps(){
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.annotations.Annotations.Serialize;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
|
|
@ -10,6 +10,7 @@ public class Administration{
|
|||
/** All player info. Maps UUIDs to info. This persists throughout restarts. */
|
||||
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
|
||||
private Array<String> bannedIPs = new Array<>();
|
||||
private Array<String> whitelist = new Array<>();
|
||||
|
||||
public Administration(){
|
||||
Core.settings.defaults(
|
||||
|
|
@ -20,9 +21,16 @@ public class Administration{
|
|||
load();
|
||||
}
|
||||
|
||||
public int getPlayerLimit(){
|
||||
return Core.settings.getInt("playerlimit", 0);
|
||||
}
|
||||
|
||||
public void setPlayerLimit(int limit){
|
||||
Core.settings.putSave("playerlimit", limit);
|
||||
}
|
||||
|
||||
public void setStrict(boolean on){
|
||||
Core.settings.put("strict", on);
|
||||
Core.settings.save();
|
||||
Core.settings.putSave("strict", on);
|
||||
}
|
||||
|
||||
public boolean getStrict(){
|
||||
|
|
@ -186,6 +194,36 @@ public class Administration{
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean isWhitelistEnabled(){
|
||||
return Core.settings.getBool("whitelist", false);
|
||||
}
|
||||
|
||||
public void setWhitelist(boolean enabled){
|
||||
Core.settings.putSave("whitelist", enabled);
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String id, String usid){
|
||||
return !isWhitelistEnabled() || whitelist.contains(usid + id);
|
||||
}
|
||||
|
||||
public boolean whitelist(String id){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
if(whitelist.contains(info.adminUsid + id)) return false;
|
||||
whitelist.add(info.adminUsid + id);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unwhitelist(String id){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
if(whitelist.contains(info.adminUsid + id)){
|
||||
whitelist.remove(info.adminUsid + id);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isIPBanned(String ip){
|
||||
return bannedIPs.contains(ip, false) || (findByIP(ip) != null && findByIP(ip).banned);
|
||||
}
|
||||
|
|
@ -242,6 +280,10 @@ public class Administration{
|
|||
return null;
|
||||
}
|
||||
|
||||
public Array<PlayerInfo> getWhitelisted(){
|
||||
return playerInfo.values().toArray().select(p -> isWhitelisted(p.id, p.adminUsid));
|
||||
}
|
||||
|
||||
private PlayerInfo getCreateInfo(String id){
|
||||
if(playerInfo.containsKey(id)){
|
||||
return playerInfo.get(id);
|
||||
|
|
@ -256,6 +298,7 @@ public class Administration{
|
|||
public void save(){
|
||||
Core.settings.putObject("player-info", playerInfo);
|
||||
Core.settings.putObject("banned-ips", bannedIPs);
|
||||
Core.settings.putObject("whitelisted", whitelist);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
|
|
@ -263,6 +306,7 @@ public class Administration{
|
|||
private void load(){
|
||||
playerInfo = Core.settings.getObject("player-info", ObjectMap.class, ObjectMap::new);
|
||||
bannedIPs = Core.settings.getObject("banned-ips", Array.class, Array::new);
|
||||
whitelist = Core.settings.getObject("whitelisted", Array.class, Array::new);
|
||||
}
|
||||
|
||||
@Serialize
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.mindustry.game.*;
|
||||
|
||||
public class Host{
|
||||
public final String name;
|
||||
public final String address;
|
||||
public final String mapname;
|
||||
public final int wave;
|
||||
public final int players;
|
||||
public final int players, playerLimit;
|
||||
public final int version;
|
||||
public final String versionType;
|
||||
public final Gamemode mode;
|
||||
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType){
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.players = players;
|
||||
|
|
@ -17,5 +20,7 @@ public class Host{
|
|||
this.wave = wave;
|
||||
this.version = version;
|
||||
this.versionType = versionType;
|
||||
this.playerLimit = playerLimit;
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.entities.Entities;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.JsonIO;
|
||||
import io.anuke.mindustry.io.SaveIO;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
|
|
@ -71,8 +69,9 @@ public class NetworkIO{
|
|||
buffer.putInt(state.wave);
|
||||
buffer.putInt(Version.build);
|
||||
writeString(buffer, Version.type);
|
||||
//TODO additional information:
|
||||
// - gamemode ID/name (just pick the closest one?)
|
||||
|
||||
buffer.put((byte)Gamemode.bestFit(state.rules).ordinal());
|
||||
buffer.putInt(netServer.admins.getPlayerLimit());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
|
@ -83,13 +82,15 @@ public class NetworkIO{
|
|||
int wave = buffer.getInt();
|
||||
int version = buffer.getInt();
|
||||
String vertype = readString(buffer);
|
||||
Gamemode gamemode = Gamemode.all[buffer.get()];
|
||||
int limit = buffer.getInt();
|
||||
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype);
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype, gamemode, limit);
|
||||
}
|
||||
|
||||
private static void writeString(ByteBuffer buffer, String string, int maxlen){
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
//truncating this way may lead to wierd encoding errors at the ends of strings...
|
||||
//todo truncating this way may lead to wierd encoding errors at the ends of strings...
|
||||
if(bytes.length > maxlen){
|
||||
bytes = Arrays.copyOfRange(bytes, 0, maxlen);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public class Packets{
|
|||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
|
|
@ -52,6 +52,7 @@ public class Packets{
|
|||
|
||||
public static class Disconnect implements Packet{
|
||||
public int id;
|
||||
public String reason;
|
||||
|
||||
@Override
|
||||
public boolean isImportant(){
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
package io.anuke.mindustry.plugin;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.pluginDirectory;
|
||||
|
|
@ -50,12 +49,9 @@ public class Plugins{
|
|||
|
||||
PluginMeta meta = JsonIO.read(PluginMeta.class, metaf.readString());
|
||||
|
||||
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
|
||||
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(classLoader, jar.file().toURI().toURL());
|
||||
|
||||
Class<?> main = Class.forName(meta.main);
|
||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
|
||||
Class<?> main = classLoader.loadClass(meta.main);
|
||||
return new LoadedPlugin(jar, zip, (Plugin)main.newInstance(), meta);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class Zone extends UnlockableContent{
|
|||
|
||||
@Override
|
||||
public void load(){
|
||||
preview = Core.atlas.find(name);
|
||||
preview = Core.atlas.find("zone-" + name);
|
||||
}
|
||||
|
||||
public Rules getRules(){
|
||||
|
|
|
|||
|
|
@ -13,11 +13,15 @@ public class ItemDisplay extends Table{
|
|||
this(item, 0);
|
||||
}
|
||||
|
||||
public ItemDisplay(Item item, int amount){
|
||||
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4);
|
||||
add(item.localizedName()).padLeft(4);
|
||||
public ItemDisplay(Item item, int amount, boolean showName){
|
||||
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4).padRight(amount > 99 ? 12 : 0);
|
||||
if(showName) add(item.localizedName()).padLeft(4 + amount > 99 ? 4 : 0);
|
||||
|
||||
this.item = item;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public ItemDisplay(Item item, int amount){
|
||||
this(item, amount, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class LiquidDisplay extends Table{
|
|||
t.add(Strings.autoFixed(amount, 1));
|
||||
add(t);
|
||||
}
|
||||
}}).size(8 * 4).padRight(3);
|
||||
}}).size(8 * 4).padRight(3 + (amount != 0 && Strings.autoFixed(amount, 1).length() > 2 ? 8 : 0));
|
||||
|
||||
if(perSecond){
|
||||
add(StatUnit.perSecond.localized()).padLeft(2).padRight(5).color(Color.LIGHT_GRAY);
|
||||
|
|
|
|||
|
|
@ -182,10 +182,10 @@ public class JoinDialog extends FloatingDialog{
|
|||
void setupServer(Server server, Host host){
|
||||
server.lastHost = host;
|
||||
server.content.clear();
|
||||
server.content.table(t -> setupHostTable(t, host)).expand().left().bottom().padLeft(12f).padBottom(8);
|
||||
buildServer(host, server.content);
|
||||
}
|
||||
|
||||
void setupHostTable(Table content, Host host){
|
||||
void buildServer(Host host, Table content){
|
||||
String versionString;
|
||||
|
||||
if(host.version == -1){
|
||||
|
|
@ -202,12 +202,14 @@ public class JoinDialog extends FloatingDialog{
|
|||
versionString = Core.bundle.format("server.version", host.version, host.versionType);
|
||||
}
|
||||
|
||||
content.add("[lightgray]" + host.name + " " + versionString).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
content.row();
|
||||
content.add("[lightgray]" + (host.players != 1 ? Core.bundle.format("players", host.players == 0 ? host.players : "[accent]" + host.players + "[lightgray]") : Core.bundle.format("players.single", "[accent]" + host.players + "[lightgray]"))).left();
|
||||
content.row();
|
||||
content.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + Core.bundle.format("save.wave", host.wave)).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
|
||||
content.table(t -> {
|
||||
t.add("[lightgray]" + host.name + " " + versionString).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
t.row();
|
||||
t.add("[lightgray]" + (Core.bundle.format("players" + (host.players == 1 ? ".single" : ""), (host.players == 0 ? "[lightgray]" : "[accent]") + host.players + (host.playerLimit > 0 ? "[lightgray]/[accent]" + host.playerLimit : "")+ "[lightgray]"))).left();
|
||||
t.row();
|
||||
t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + host.mode.toString()).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
}).expand().left().bottom().padLeft(12f).padBottom(8);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
|
|
@ -275,6 +277,9 @@ public class JoinDialog extends FloatingDialog{
|
|||
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();
|
||||
Net.discoverServers(this::addLocalHost, this::finishLocalHosts);
|
||||
for(String host : defaultServers){
|
||||
Net.pingHost(host, port, this::addLocalHost, e -> {});
|
||||
}
|
||||
}
|
||||
|
||||
void finishLocalHosts(){
|
||||
|
|
@ -299,11 +304,10 @@ public class JoinDialog extends FloatingDialog{
|
|||
|
||||
local.row();
|
||||
|
||||
local.addButton(b -> {
|
||||
b.margin(5f);
|
||||
b.left();
|
||||
setupHostTable(b, host);
|
||||
}, "clear", () -> connect(host.address, port)).width(w).pad(4f).get();
|
||||
TextButton button = local.addButton("", "clear", () -> connect(host.address, port))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
}
|
||||
|
||||
void connect(String ip, int port){
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@ public class LoadDialog extends FloatingDialog{
|
|||
meta.row();
|
||||
meta.labelWrap(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().name())));
|
||||
meta.row();
|
||||
meta.labelWrap(Core.bundle.format("save.wave", color + slot.getWave()));
|
||||
meta.labelWrap(slot.mode().toString() + " /" + color + " " + Core.bundle.format("save.wave", color + slot.getWave()));
|
||||
meta.row();
|
||||
meta.labelWrap(() -> Core.bundle.format("save.autosave", color + Core.bundle.get(slot.isAutosave() ? "on" : "off")));
|
||||
meta.row();
|
||||
meta.labelWrap(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
|
||||
meta.row();
|
||||
meta.labelWrap(Core.bundle.format("save.date", color + slot.getDate()));
|
||||
meta.labelWrap(color + slot.getDate());
|
||||
meta.row();
|
||||
}).left().growX().width(250f);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,49 +62,52 @@ public class MapsDialog extends FloatingDialog{
|
|||
if(!ios){
|
||||
buttons.addImageTextButton("$editor.importmap", "icon-load", iconsize, () -> {
|
||||
platform.showFileChooser("$editor.importmap", "Map File", file -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showError("$editor.errorimage");
|
||||
return;
|
||||
}
|
||||
ui.loadAnd(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showError("$editor.errorimage");
|
||||
return;
|
||||
}
|
||||
|
||||
Map map;
|
||||
if(file.extension().equalsIgnoreCase(mapExtension)){
|
||||
map = MapIO.createMap(file, true);
|
||||
}else{
|
||||
map = maps.makeLegacyMap(file);
|
||||
}
|
||||
Map map;
|
||||
if(file.extension().equalsIgnoreCase(mapExtension)){
|
||||
map = MapIO.createMap(file, true);
|
||||
}else{
|
||||
map = maps.makeLegacyMap(file);
|
||||
}
|
||||
|
||||
//when you attempt to import a save, it will have no name, so generate one
|
||||
String name = map.tags.getOr("name", () -> {
|
||||
String result = "unknown";
|
||||
int number = 0;
|
||||
while(maps.byName(result + number++) != null) ;
|
||||
return result + number;
|
||||
});
|
||||
|
||||
//this will never actually get called, but it remains just in case
|
||||
if(name == null){
|
||||
ui.showError("$editor.errorname");
|
||||
return;
|
||||
}
|
||||
|
||||
Map conflict = maps.all().find(m -> m.name().equals(name));
|
||||
|
||||
if(conflict != null && !conflict.custom){
|
||||
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
maps.importMap(file);
|
||||
setup();
|
||||
});
|
||||
//when you attempt to import a save, it will have no name, so generate one
|
||||
String name = map.tags.getOr("name", () -> {
|
||||
String result = "unknown";
|
||||
int number = 0;
|
||||
while(maps.byName(result + number++) != null);
|
||||
return result + number;
|
||||
});
|
||||
}else{
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
}
|
||||
|
||||
//this will never actually get called, but it remains just in case
|
||||
if(name == null){
|
||||
ui.showError("$editor.errorname");
|
||||
return;
|
||||
}
|
||||
|
||||
Map conflict = maps.all().find(m -> m.name().equals(name));
|
||||
|
||||
if(conflict != null && !conflict.custom){
|
||||
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
maps.removeMap(conflict);
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
});
|
||||
});
|
||||
}else{
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}, true, FileChooser.anyMapFiles);
|
||||
}).size(210f, 64f);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import io.anuke.arc.graphics.*;
|
|||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.renderer;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ public class MinimapDialog extends FloatingDialog{
|
|||
renderer.minimap.drawEntities(x, y, width, height);
|
||||
}
|
||||
}).grow();
|
||||
}).size(Math.min(Core.graphics.getWidth() / 1.1f, Core.graphics.getHeight() / 1.3f)).padTop(-20f);
|
||||
}).size(Math.min(Core.graphics.getWidth() / 1.1f, Core.graphics.getHeight() / 1.3f) / UnitScl.dp.scl(1f)).padTop(-20f);
|
||||
|
||||
cont.addListener(new InputListener(){
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
|||
t.row();
|
||||
|
||||
//iOS doesn't have a file chooser.
|
||||
if(!ios){
|
||||
//if(!ios){
|
||||
t.addButton("$data.import", style, () -> ui.showConfirm("$confirm", "$data.import.confirm", () -> platform.showFileChooser("$data.import", "Zip Files", file -> {
|
||||
try{
|
||||
data.importData(file);
|
||||
|
|
@ -151,7 +151,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
|||
ui.showError(Strings.parseException(e, true));
|
||||
}
|
||||
}, true, f -> f.equalsIgnoreCase("zip"))));
|
||||
}
|
||||
//}
|
||||
});
|
||||
|
||||
ScrollPane pane = new ScrollPane(prefs);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class BlockInventoryFragment extends Fragment{
|
|||
|
||||
@Remote(called = Loc.server, targets = Loc.both, forward = true)
|
||||
public static void requestItem(Player player, Tile tile, Item item, int amount){
|
||||
if(player == null || tile == null) return;
|
||||
if(player == null || tile == null || !player.timer.get(Player.timerTransfer, 20)) return;
|
||||
|
||||
int removed = tile.block().removeStack(tile, item, amount);
|
||||
|
||||
|
|
@ -71,6 +71,8 @@ public class BlockInventoryFragment extends Fragment{
|
|||
}
|
||||
|
||||
public void hide(){
|
||||
if(table == null) return;
|
||||
|
||||
table.actions(Actions.scaleTo(0f, 1f, 0.06f, Interpolation.pow3Out), Actions.run(() -> {
|
||||
table.clearChildren();
|
||||
table.clearListeners();
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import io.anuke.arc.util.pooling.*;
|
|||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
|
|
@ -32,6 +31,7 @@ import io.anuke.mindustry.world.blocks.*;
|
|||
import io.anuke.mindustry.world.blocks.power.*;
|
||||
import io.anuke.mindustry.world.consumers.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
import io.anuke.mindustry.world.meta.values.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ public class Block extends BlockStorage{
|
|||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
}
|
||||
|
||||
protected float drawPlaceText(String text, int x, int y, boolean valid){
|
||||
public float drawPlaceText(String text, int x, int y, boolean valid){
|
||||
if(renderer.pixelator.enabled()) return 0;
|
||||
|
||||
Color color = valid ? Pal.accent : Pal.remove;
|
||||
|
|
@ -476,7 +476,10 @@ public class Block extends BlockStorage{
|
|||
public void setStats(){
|
||||
stats.add(BlockStat.size, "{0}x{0}", size);
|
||||
stats.add(BlockStat.health, health, StatUnit.none);
|
||||
stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds);
|
||||
if(isBuildable()){
|
||||
stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds);
|
||||
stats.add(BlockStat.buildCost, new ItemListValue(false, buildRequirements));
|
||||
}
|
||||
|
||||
consumes.display(stats);
|
||||
|
||||
|
|
@ -581,7 +584,7 @@ public class Block extends BlockStorage{
|
|||
});
|
||||
}
|
||||
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, power, tilesize * size / 2f, Pal.darkFlame);
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness * 3.5f, power, tilesize * size / 2f, Pal.darkFlame);
|
||||
if(!tile.floor().solid && !tile.floor().isLiquid){
|
||||
RubbleDecal.create(tile.drawx(), tile.drawy(), size);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ public abstract class BlockStorage extends UnlockableContent{
|
|||
|
||||
public int itemCapacity = 10;
|
||||
public float liquidCapacity = 10f;
|
||||
public float liquidFlowFactor = 4.9f;
|
||||
|
||||
public final BlockStats stats = new BlockStats();
|
||||
public final BlockBars bars = new BlockBars();
|
||||
|
|
|
|||
71
core/src/io/anuke/mindustry/world/blocks/RespawnBlock.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package io.anuke.mindustry.world.blocks;
|
||||
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
public class RespawnBlock{
|
||||
|
||||
public static void drawRespawn(Tile tile, float heat, float progress, float time, Player player, Mech to){
|
||||
progress = Mathf.clamp(progress);
|
||||
|
||||
Draw.color(Pal.darkMetal);
|
||||
Lines.stroke(2f * heat);
|
||||
Fill.poly(tile.drawx(), tile.drawy(), 4, 10f * heat);
|
||||
|
||||
Draw.reset();
|
||||
if(player != null){
|
||||
TextureRegion region = to.iconRegion;
|
||||
|
||||
Draw.color(0f, 0f, 0f, 0.4f * progress);
|
||||
Draw.rect("circle-shadow", tile.drawx(), tile.drawy(), region.getWidth() / 3f, region.getWidth() / 3f);
|
||||
Draw.color();
|
||||
|
||||
Shaders.build.region = region;
|
||||
Shaders.build.progress = progress;
|
||||
Shaders.build.color.set(Pal.accent);
|
||||
Shaders.build.time = -time / 10f;
|
||||
|
||||
Draw.shader(Shaders.build, true);
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.shader();
|
||||
|
||||
Draw.color(Pal.accentBack);
|
||||
|
||||
float pos = Mathf.sin(time, 6f, 8f);
|
||||
|
||||
Lines.lineAngleCenter(tile.drawx() + pos, tile.drawy(), 90, 16f - Math.abs(pos) * 2f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
Lines.stroke(2f * heat);
|
||||
|
||||
Draw.color(Pal.accentBack);
|
||||
Lines.poly(tile.drawx(), tile.drawy(), 4, 8f * heat);
|
||||
|
||||
float oy = -7f, len = 6f * heat;
|
||||
Lines.stroke(5f);
|
||||
Draw.color(Pal.darkMetal);
|
||||
Lines.line(tile.drawx() - len, tile.drawy() + oy, tile.drawx() + len, tile.drawy() + oy, CapStyle.none);
|
||||
for(int i : Mathf.signs){
|
||||
Fill.tri(tile.drawx() + len * i, tile.drawy() + oy - Lines.getStroke()/2f, tile.drawx() + len * i, tile.drawy() + oy + Lines.getStroke()/2f, tile.drawx() + (len + Lines.getStroke() * heat) * i, tile.drawy() + oy);
|
||||
}
|
||||
|
||||
Lines.stroke(3f);
|
||||
Draw.color(Pal.accent);
|
||||
Lines.line(tile.drawx() - len, tile.drawy() + oy, tile.drawx() - len + len*2 * progress, tile.drawy() + oy, CapStyle.none);
|
||||
for(int i : Mathf.signs){
|
||||
Fill.tri(tile.drawx() + len * i, tile.drawy() + oy - Lines.getStroke()/2f, tile.drawx() + len * i, tile.drawy() + oy + Lines.getStroke()/2f, tile.drawx() + (len + Lines.getStroke() * heat) * i, tile.drawy() + oy);
|
||||
}
|
||||
Draw.reset();
|
||||
|
||||
if(Net.active() && player != null){
|
||||
tile.block().drawPlaceText(player.name, tile.x, tile.y - (Math.max((tile.block().size-1)/2, 0)), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class CooledTurret extends Turret{
|
|||
TurretEntity entity = tile.entity();
|
||||
Liquid liquid = entity.liquids.current();
|
||||
|
||||
float used = Math.min(Math.min(entity.liquids.get(liquid), maxUsed * Time.delta()), Math.max(0, ((reload - entity.reload) / coolantMultiplier) / liquid.heatCapacity));
|
||||
float used = Math.min(Math.min(entity.liquids.get(liquid), maxUsed * Time.delta()), Math.max(0, ((reload - entity.reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed(tile);
|
||||
entity.reload += (used * liquid.heatCapacity) / liquid.heatCapacity;
|
||||
entity.liquids.remove(liquid, used);
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ public abstract class Turret extends Block{
|
|||
protected void turnToTarget(Tile tile, float targetRot){
|
||||
TurretEntity entity = tile.entity();
|
||||
|
||||
entity.rotation = Angles.moveToward(entity.rotation, targetRot, rotatespeed * entity.delta());
|
||||
entity.rotation = Angles.moveToward(entity.rotation, targetRot, rotatespeed * entity.delta() * baseReloadSpeed(tile));
|
||||
}
|
||||
|
||||
public boolean shouldTurn(Tile tile){
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class ItemLiquidGenerator extends PowerGenerator{
|
|||
protected Effects.Effect explodeEffect = Fx.generatespark;
|
||||
protected Color heatColor = Color.valueOf("ff9b59");
|
||||
protected TextureRegion topRegion, liquidRegion;
|
||||
protected boolean randomlyExplode = false;
|
||||
protected boolean randomlyExplode = true;
|
||||
|
||||
public ItemLiquidGenerator(boolean hasItems, boolean hasLiquids, String name){
|
||||
super(name);
|
||||
|
|
@ -127,8 +127,10 @@ public class ItemLiquidGenerator extends PowerGenerator{
|
|||
|
||||
if(randomlyExplode && Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.5f))){
|
||||
//this block is run last so that in the event of a block destruction, no code relies on the block type
|
||||
entity.damage(Mathf.random(11f));
|
||||
Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f));
|
||||
Core.app.post(() -> {
|
||||
entity.damage(Mathf.random(11f));
|
||||
Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f));
|
||||
});
|
||||
}
|
||||
}else{
|
||||
entity.productionEfficiency = 0.0f;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ public class Pump extends LiquidBlock{
|
|||
public Pump(String name){
|
||||
super(name);
|
||||
layer = Layer.overlay;
|
||||
liquidFlowFactor = 3f;
|
||||
group = BlockGroup.liquids;
|
||||
floating = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ package io.anuke.mindustry.world.blocks.storage;
|
|||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
|
|
@ -16,6 +14,7 @@ import io.anuke.mindustry.graphics.*;
|
|||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
|
@ -32,6 +31,7 @@ public class CoreBlock extends StorageBlock{
|
|||
flags = EnumSet.of(BlockFlag.target, BlockFlag.producer);
|
||||
activeSound = Sounds.respawning;
|
||||
activeSoundVolume = 1f;
|
||||
layer = Layer.overlay;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
|
|
@ -84,41 +84,11 @@ public class CoreBlock extends StorageBlock{
|
|||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
public void drawLayer(Tile tile){
|
||||
CoreEntity entity = tile.entity();
|
||||
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
|
||||
if(entity.heat > 0){
|
||||
Draw.color(Pal.darkMetal);
|
||||
Lines.stroke(2f * entity.heat);
|
||||
Lines.poly(tile.drawx(), tile.drawy(), 4, 8f * entity.heat);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
if(entity.spawnPlayer != null){
|
||||
Unit player = entity.spawnPlayer;
|
||||
|
||||
TextureRegion region = player.getIconRegion();
|
||||
|
||||
Shaders.build.region = region;
|
||||
Shaders.build.progress = entity.progress;
|
||||
Shaders.build.color.set(Pal.accent);
|
||||
Shaders.build.time = -entity.time / 10f;
|
||||
|
||||
Draw.shader(Shaders.build, true);
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.shader();
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
Lines.lineAngleCenter(
|
||||
tile.drawx() + Mathf.sin(entity.time, 6f, Vars.tilesize / 3f * size),
|
||||
tile.drawy(),
|
||||
90,
|
||||
size * Vars.tilesize / 2f);
|
||||
|
||||
Draw.reset();
|
||||
if(entity.heat > 0.001f){
|
||||
RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.spawnPlayer, mech);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +143,11 @@ public class CoreBlock extends StorageBlock{
|
|||
float time;
|
||||
float heat;
|
||||
|
||||
@Override
|
||||
public boolean hasUnit(Unit unit){
|
||||
return unit == spawnPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawning(Player player){
|
||||
if(!netServer.isWaitingForPlayers() && spawnPlayer == null){
|
||||
|
|
|
|||
|
|
@ -1,32 +1,24 @@
|
|||
package io.anuke.mindustry.world.blocks.units;
|
||||
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.content.Mechs;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.traits.SpawnerTrait;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.graphics.Shaders;
|
||||
import io.anuke.mindustry.type.Mech;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockStat;
|
||||
import io.anuke.mindustry.world.meta.StatUnit;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.mobile;
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class MechPad extends Block{
|
||||
protected Mech mech;
|
||||
|
|
@ -37,6 +29,7 @@ public class MechPad extends Block{
|
|||
update = true;
|
||||
solid = false;
|
||||
hasPower = true;
|
||||
layer = Layer.overlay;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -110,32 +103,11 @@ public class MechPad extends Block{
|
|||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
public void drawLayer(Tile tile){
|
||||
MechFactoryEntity entity = tile.entity();
|
||||
|
||||
Draw.rect(Core.atlas.find(name), tile.drawx(), tile.drawy());
|
||||
|
||||
if(entity.player != null){
|
||||
TextureRegion region = (!entity.sameMech && entity.player.mech == mech ? Mechs.starter.iconRegion : mech.iconRegion);
|
||||
|
||||
Shaders.build.region = region;
|
||||
Shaders.build.progress = entity.progress;
|
||||
Shaders.build.time = -entity.time / 5f;
|
||||
Shaders.build.color.set(Pal.accent);
|
||||
|
||||
Draw.shader(Shaders.build);
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.shader();
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
Lines.lineAngleCenter(
|
||||
tile.drawx() + Mathf.sin(entity.time, 6f, Vars.tilesize / 3f * size),
|
||||
tile.drawy(),
|
||||
90,
|
||||
size * Vars.tilesize / 2f + 1f);
|
||||
|
||||
Draw.reset();
|
||||
RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.player, (!entity.sameMech && entity.player.mech == mech ? Mechs.starter : mech));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +142,11 @@ public class MechPad extends Block{
|
|||
float time;
|
||||
float heat;
|
||||
|
||||
@Override
|
||||
public boolean hasUnit(Unit unit){
|
||||
return unit == player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawning(Player unit){
|
||||
if(player == null){
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public enum BlockStat{
|
|||
health(StatCategory.general),
|
||||
size(StatCategory.general),
|
||||
buildTime(StatCategory.general),
|
||||
buildCost(StatCategory.general),
|
||||
|
||||
itemCapacity(StatCategory.items),
|
||||
itemsMoved(StatCategory.items),
|
||||
|
|
|
|||
|
|
@ -7,15 +7,21 @@ import io.anuke.mindustry.world.meta.StatValue;
|
|||
|
||||
public class ItemListValue implements StatValue{
|
||||
private final ItemStack[] stacks;
|
||||
private final boolean displayName;
|
||||
|
||||
public ItemListValue(ItemStack... stacks){
|
||||
this(true, stacks);
|
||||
}
|
||||
|
||||
public ItemListValue(boolean displayName, ItemStack... stacks){
|
||||
this.stacks = stacks;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Table table){
|
||||
for(ItemStack stack : stacks){
|
||||
table.add(new ItemDisplay(stack.item, stack.amount)).padRight(5);
|
||||
table.add(new ItemDisplay(stack.item, stack.amount, displayName)).padRight(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
fastlane/metadata/android/fr-FR/full_description.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Crée des chaînes de ravitaillement pour tes défenses, produit les matériaux de construction pour aggrandir et protéger tes batiments contre des vagues d'ennemis. Joue avec tes amis grâce à des jeux multijoueurs co-op cross-plateforme, ou défie les dans des parties en PvP par équipe.
|
||||
|
||||
Caractéristiques:
|
||||
- 24 cartes dans le jeu de base
|
||||
- Une campagne, complète avec un arbre de recherche et des zones à débloquer
|
||||
- 4 puissantes vagues de boss à vaincre
|
||||
- Systèmes de transport d'énergie, liquides et objets
|
||||
- 19 différent types de drones, méchas et vaisseaux
|
||||
- 120+ blocs technologiques à maîtriser
|
||||
- 75+ différents blocs environnementaux
|
||||
- Multijoueur cross-plateforme via réseau local or serveurs dédiés
|
||||
- Règles de jeu personnalisables: Changez le coût des structures, les stats des ennemis, les ressources de départ, fréquence des vagues et plus
|
||||
- Un éditeur puissant dotés d'outils pour générer aléatoirement des minéraux, le terrain, des décorations et appliquer une symétrie de terrain.
|
||||
- Personnaliser les vagues d'ennemis
|
||||
1
fastlane/metadata/android/fr-FR/short_description.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
L'industrie au service de ce tower defense.
|
||||
1
fastlane/metadata/android/fr-FR/title.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Mindustry
|
||||
0
fastlane/metadata/android/fr-FR/video.txt
Normal file
|
|
@ -73,6 +73,23 @@
|
|||
<string>io.anuke.mindustry.mapfile</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>icon-72.png</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Zip Data File</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.archive</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</array>
|
||||
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package io.anuke.mindustry;
|
|||
import com.badlogic.gdx.backends.iosrobovm.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
|
|
@ -13,6 +14,7 @@ import io.anuke.mindustry.net.Net;
|
|||
import io.anuke.mindustry.net.*;
|
||||
import org.robovm.apple.foundation.*;
|
||||
import org.robovm.apple.uikit.*;
|
||||
import org.robovm.objc.block.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
|
@ -38,6 +40,51 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||
IOSApplicationConfiguration config = new IOSApplicationConfiguration();
|
||||
return new IOSApplication(new ClientLauncher(){
|
||||
|
||||
@Override
|
||||
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, Predicate<String> filetype){
|
||||
UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController();
|
||||
cont.setAllowsDocumentCreation(false);
|
||||
cont.setDelegate(new UIDocumentBrowserViewControllerDelegate(){
|
||||
@Override
|
||||
public void didPickDocumentURLs(UIDocumentBrowserViewController controller, NSArray<NSURL> documentURLs){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didPickDocumentsAtURLs(UIDocumentBrowserViewController controller, NSArray<NSURL> documentURLs){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didRequestDocumentCreationWithHandler(UIDocumentBrowserViewController controller, VoidBlock2<NSURL, UIDocumentBrowserImportMode> importHandler){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didImportDocument(UIDocumentBrowserViewController controller, NSURL sourceURL, NSURL destinationURL){
|
||||
cons.accept(Core.files.absolute(destinationURL.getAbsoluteString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failedToImportDocument(UIDocumentBrowserViewController controller, NSURL documentURL, NSError error){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public NSArray<UIActivity> applicationActivities(UIDocumentBrowserViewController controller, NSArray<NSURL> documentURLs){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void willPresentActivityViewController(UIDocumentBrowserViewController controller, UIActivityViewController activityViewController){
|
||||
|
||||
}
|
||||
});
|
||||
UIApplication.getSharedApplication().getKeyWindow().getRootViewController().presentViewController(cont, true, () -> {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shareFile(FileHandle file){
|
||||
Log.info("Attempting to share file " + file);
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ public class ArcNetClient implements ClientProvider{
|
|||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection){
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +182,8 @@ public class ArcNetClient implements ClientProvider{
|
|||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,13 @@ public class ArcNetServer implements ServerProvider{
|
|||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection){
|
||||
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);
|
||||
|
|
@ -102,6 +103,34 @@ public class ArcNetServer implements ServerProvider{
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStream(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();
|
||||
|
|
@ -159,7 +188,7 @@ public class ArcNetServer implements ServerProvider{
|
|||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
Log.info("Error sending packet. Disconnecting invalid client!");
|
||||
connection.close();
|
||||
connection.close(DcReason.error);
|
||||
|
||||
ArcConnection k = getByArcID(connection.getID());
|
||||
if(k != null) connections.remove(k);
|
||||
|
|
@ -168,7 +197,7 @@ public class ArcNetServer implements ServerProvider{
|
|||
|
||||
@Override
|
||||
public void close(){
|
||||
if(connection.isConnected()) connection.close();
|
||||
if(connection.isConnected()) connection.close(DcReason.closed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class ServerControl implements ApplicationListener{
|
|||
private static final int commandSocketPort = 6859;
|
||||
|
||||
private final CommandHandler handler = new CommandHandler("");
|
||||
private final FileHandle logFolder = Core.files.local("logs/");
|
||||
private final FileHandle logFolder = Core.settings.getDataDirectory().child("logs/");
|
||||
private final io.anuke.mindustry.plugin.Plugins plugins = new Plugins();
|
||||
|
||||
private FileHandle currentLogFile;
|
||||
|
|
@ -401,6 +401,68 @@ public class ServerControl implements ApplicationListener{
|
|||
info("Server name is now &lc'{0}'.", arg[0]);
|
||||
});
|
||||
|
||||
handler.register("playerlimit", "[off/somenumber]", "Set the server player limit.", arg -> {
|
||||
if(arg.length == 0){
|
||||
info("Player limit is currently &lc{0}.", netServer.admins.getPlayerLimit() == 0 ? "off" : netServer.admins.getPlayerLimit());
|
||||
return;
|
||||
}
|
||||
if(arg[0].equals("off")){
|
||||
netServer.admins.setPlayerLimit(0);
|
||||
info("Player limit disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(Strings.canParsePostiveInt(arg[0]) && Strings.parseInt(arg[0]) > 0){
|
||||
int lim = Strings.parseInt(arg[0]);
|
||||
netServer.admins.setPlayerLimit(lim);
|
||||
info("Player limit is now &lc{0}.", lim);
|
||||
}else{
|
||||
err("Limit must be a number above 0.");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("whitelist", "[on/off...]", "Enable/disable whitelisting.", arg -> {
|
||||
if(arg.length == 0){
|
||||
info("Whitelist is currently &lc{0}.", netServer.admins.isWhitelistEnabled() ? "on" : "off");
|
||||
return;
|
||||
}
|
||||
boolean on = arg[0].equalsIgnoreCase("on");
|
||||
netServer.admins.setWhitelist(on);
|
||||
info("Whitelist is now &lc{0}.", on ? "on" : "off");
|
||||
});
|
||||
|
||||
handler.register("whitelisted", "List the entire whitelist.", arg -> {
|
||||
if(netServer.admins.getWhitelisted().isEmpty()){
|
||||
info("&lyNo whitelisted players found.");
|
||||
return;
|
||||
}
|
||||
|
||||
info("&lyWhitelist:");
|
||||
netServer.admins.getWhitelisted().each(p -> Log.info("- &ly{0}", p.lastName));
|
||||
});
|
||||
|
||||
handler.register("whitelist-add", "<ID>", "Add a player to the whitelist by ID.", arg -> {
|
||||
PlayerInfo info = netServer.admins.getInfoOptional(arg[0]);
|
||||
if(info == null){
|
||||
err("Player ID not found. You must use the ID displayed when a player joins a server.");
|
||||
return;
|
||||
}
|
||||
|
||||
netServer.admins.whitelist(arg[0]);
|
||||
info("Player &ly'{0}'&lg has been whitelisted.", info.lastName);
|
||||
});
|
||||
|
||||
handler.register("whitelist-remove", "<ID>", "Remove a player to the whitelist by ID.", arg -> {
|
||||
PlayerInfo info = netServer.admins.getInfoOptional(arg[0]);
|
||||
if(info == null){
|
||||
err("Player ID not found. You must use the ID displayed when a player joins a server.");
|
||||
return;
|
||||
}
|
||||
|
||||
netServer.admins.unwhitelist(arg[0]);
|
||||
info("Player &ly'{0}'&lg has been un-whitelisted.", info.lastName);
|
||||
});
|
||||
|
||||
handler.register("crashreport", "<on/off>", "Disables or enables automatic crash reporting", arg -> {
|
||||
boolean value = arg[0].equalsIgnoreCase("on");
|
||||
Core.settings.put("crashreport", value);
|
||||
|
|
|
|||