Security changes: placement verification, banning, unfinished admins
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 257 B |
Binary file not shown.
|
After Width: | Height: | Size: 276 B |
@@ -25,6 +25,7 @@ text.server.kicked.kick=You have been kicked from the server!
|
||||
text.server.kicked.invalidPassword=Invalid password!
|
||||
text.server.kicked.clientOutdated=Outdated client! Update your game!
|
||||
text.server.kicked.serverOutdated=Outdated server! Ask the host to update!
|
||||
text.server.kicked.banned=You are banned on this server.
|
||||
text.server.connected={0} has joined.
|
||||
text.server.disconnected={0} has disconnected.
|
||||
text.nohost=Can't host server on a custom map!
|
||||
@@ -37,10 +38,18 @@ text.server.refreshing=Refreshing server
|
||||
text.hosts.none=[lightgray]No LAN games found!
|
||||
text.host.invalid=[scarlet]Can't connect to host.
|
||||
text.server.friendlyfire=Friendly Fire
|
||||
text.server.bans=Bans
|
||||
text.server.bans.none=No banned players found!
|
||||
text.server.admins=Admins
|
||||
text.server.admins.none=No admins found!
|
||||
text.server.add=Add Server
|
||||
text.server.delete=Are you sure you want to delete this server?
|
||||
text.server.hostname=Host: {0}
|
||||
text.server.edit=Edit Server
|
||||
text.confirmban=Are you sure you want to ban 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.confirmunadmin=Are you sure you want to remove admin status from this player?
|
||||
text.joingame.byip=Join by IP...
|
||||
text.joingame.title=Join Game
|
||||
text.joingame.ip=IP:
|
||||
|
||||
+256
-242
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
@@ -1,7 +1,7 @@
|
||||
#Autogenerated file. Do not modify.
|
||||
#Fri Feb 23 21:37:45 EST 2018
|
||||
#Sun Feb 25 22:24:39 EST 2018
|
||||
version=release
|
||||
androidBuildCode=275
|
||||
androidBuildCode=282
|
||||
name=Mindustry
|
||||
code=3.3
|
||||
build=custom build
|
||||
|
||||
@@ -17,7 +17,9 @@ import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.NetworkIO;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.resource.Item;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Map;
|
||||
import io.anuke.mindustry.world.Placement;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
@@ -165,6 +167,14 @@ public class NetClient extends Module {
|
||||
ui.hudfrag.updateItems();
|
||||
});
|
||||
|
||||
Net.handleClient(PlacePacket.class, (packet) -> {
|
||||
Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
|
||||
});
|
||||
|
||||
Net.handleClient(BreakPacket.class, (packet) -> {
|
||||
Placement.breakBlock(packet.x, packet.y, true, false);
|
||||
});
|
||||
|
||||
Net.handleClient(EntitySpawnPacket.class, packet -> {
|
||||
EntityGroup group = packet.group;
|
||||
|
||||
@@ -316,6 +326,11 @@ public class NetClient extends Module {
|
||||
r.run();
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleClient(NetErrorPacket.class, packet -> {
|
||||
ui.showError(packet.message);
|
||||
disconnectQuietly();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,11 +4,8 @@ import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.resource.Recipe;
|
||||
import io.anuke.mindustry.resource.Recipes;
|
||||
import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.modules.Module;
|
||||
|
||||
@@ -25,23 +22,6 @@ public class NetCommon extends Module {
|
||||
weapon.shoot(player, packet.x, packet.y, packet.rotation);
|
||||
});
|
||||
|
||||
Net.handle(PlacePacket.class, (packet) -> {
|
||||
if(headless)
|
||||
world.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation);
|
||||
else
|
||||
control.input().placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
|
||||
|
||||
Recipe recipe = Recipes.getByResult(Block.getByID(packet.block));
|
||||
if (recipe != null) state.inventory.removeItems(recipe.requirements);
|
||||
});
|
||||
|
||||
Net.handle(BreakPacket.class, (packet) -> {
|
||||
if(headless)
|
||||
world.removeBlock(world.tile(packet.x, packet.y));
|
||||
else
|
||||
control.input().breakBlockInternal(packet.x, packet.y, false);
|
||||
});
|
||||
|
||||
Net.handle(ChatPacket.class, (packet) -> {
|
||||
ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name));
|
||||
});
|
||||
|
||||
@@ -7,14 +7,15 @@ import io.anuke.mindustry.entities.SyncEntity;
|
||||
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
||||
import io.anuke.mindustry.io.Platform;
|
||||
import io.anuke.mindustry.io.Version;
|
||||
import io.anuke.mindustry.net.Administration;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.net.NetworkIO;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.resource.UpgradeRecipes;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.resource.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Placement;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
@@ -41,6 +42,8 @@ public class NetServer extends Module{
|
||||
private final static int timerStateSync = 1;
|
||||
private final static int timerBlockSync = 2;
|
||||
|
||||
public final Administration admins = new Administration();
|
||||
|
||||
/**Maps connection IDs to players.*/
|
||||
private IntMap<Player> connections = new IntMap<>();
|
||||
private ObjectMap<String, ByteArray> weapons = new ObjectMap<>();
|
||||
@@ -51,12 +54,26 @@ public class NetServer extends Module{
|
||||
|
||||
Events.on(GameOverEvent.class, () -> weapons.clear());
|
||||
|
||||
Net.handleServer(Connect.class, (id, connect) -> {});
|
||||
Net.handleServer(Connect.class, (id, connect) -> {
|
||||
if(admins.isBanned(connect.addressTCP)){
|
||||
Net.kickConnection(id, KickReason.banned);
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
||||
if(Net.getConnection(id) == null ||
|
||||
admins.isBanned(Net.getConnection(id).address)) return;
|
||||
|
||||
admins.setKnownName(Net.getConnection(id).address, packet.name);
|
||||
|
||||
if(packet.version != Version.build && Version.build != -1){
|
||||
if(packet.version == -1){
|
||||
admins.banPlayer(Net.getConnection(id).address);
|
||||
Net.kickConnection(id, KickReason.banned);
|
||||
}else {
|
||||
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
}
|
||||
|
||||
if(packet.version != Version.build && packet.version != -1 && Version.build != -1){ //ignore 'custom builds' on both ends
|
||||
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,19 +162,41 @@ public class NetServer extends Module{
|
||||
|
||||
Net.handleServer(PlacePacket.class, (id, packet) -> {
|
||||
packet.playerid = connections.get(id).id;
|
||||
Net.sendExcept(id, packet, SendMode.tcp);
|
||||
|
||||
if(!Placement.validPlace(packet.x, packet.y, Block.getByID(packet.block))) return;
|
||||
|
||||
Recipe recipe = Recipes.getByResult(Block.getByID(packet.block));
|
||||
|
||||
if(recipe == null) return;
|
||||
|
||||
state.inventory.removeItems(recipe.requirements);
|
||||
|
||||
Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
|
||||
|
||||
Net.send(packet, SendMode.tcp);
|
||||
});
|
||||
|
||||
Net.handleServer(BreakPacket.class, (id, packet) -> {
|
||||
packet.playerid = connections.get(id).id;
|
||||
Net.sendExcept(id, packet, SendMode.tcp);
|
||||
|
||||
if(!Placement.validBreak(packet.x, packet.y)) return;
|
||||
|
||||
Placement.breakBlock(packet.x, packet.y, true, false);
|
||||
|
||||
Net.send(packet, SendMode.tcp);
|
||||
});
|
||||
|
||||
Net.handleServer(ChatPacket.class, (id, packet) -> {
|
||||
if(!Timers.get("chatFlood" + id, 20)){
|
||||
ChatPacket warn = new ChatPacket();
|
||||
warn.text = "[scarlet]You are sending messages too quickly.";
|
||||
Net.sendTo(id, warn, SendMode.tcp);
|
||||
return;
|
||||
}
|
||||
Player player = connections.get(id);
|
||||
packet.name = player.name;
|
||||
packet.id = player.id;
|
||||
Net.sendExcept(player.clientid, packet, SendMode.tcp);
|
||||
Net.send(packet, SendMode.tcp);
|
||||
});
|
||||
|
||||
Net.handleServer(UpgradePacket.class, (id, packet) -> {
|
||||
|
||||
@@ -274,6 +274,7 @@ public class Renderer extends RendererModule{
|
||||
void drawEnemyMarkers(){
|
||||
Graphics.surface(indicatorSurface);
|
||||
Draw.color(Color.RED);
|
||||
|
||||
for(Enemy enemy : enemyGroup.all()) {
|
||||
|
||||
if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y)
|
||||
|
||||
@@ -27,7 +27,10 @@ public class ThreadHandler {
|
||||
public ThreadHandler(ThreadProvider impl){
|
||||
this.impl = impl;
|
||||
|
||||
Timers.setDeltaProvider(() -> impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f);
|
||||
Timers.setDeltaProvider(() ->{
|
||||
float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f;
|
||||
return Float.isNaN(result) ? 1f : result;
|
||||
});
|
||||
}
|
||||
|
||||
public void run(Runnable r){
|
||||
|
||||
@@ -45,6 +45,8 @@ public class UI extends SceneModule{
|
||||
public ControlsDialog controls;
|
||||
public MapEditorDialog editor;
|
||||
public LanguageDialog language;
|
||||
public BansDialog bans;
|
||||
public AdminsDialog admins;
|
||||
|
||||
public final MenuFragment menufrag = new MenuFragment();
|
||||
public final ToolFragment toolfrag = new ToolFragment();
|
||||
@@ -150,6 +152,8 @@ public class UI extends SceneModule{
|
||||
paused = new PausedDialog();
|
||||
about = new AboutDialog();
|
||||
host = new HostDialog();
|
||||
bans = new BansDialog();
|
||||
admins = new AdminsDialog();
|
||||
|
||||
build.begin(scene);
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@ import io.anuke.mindustry.ai.Pathfind;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.io.Maps;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Map;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.WorldGenerator;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.DistributionBlocks;
|
||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||
@@ -22,7 +19,8 @@ import io.anuke.ucore.modules.Module;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
import io.anuke.ucore.util.Tmp;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
import static io.anuke.mindustry.Vars.control;
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
|
||||
public class World extends Module{
|
||||
private int seed;
|
||||
@@ -182,7 +180,7 @@ public class World extends Module{
|
||||
|
||||
core = WorldGenerator.generate(map.pixmap, tiles, spawns);
|
||||
|
||||
placeBlock(core.x, core.y, ProductionBlocks.core, 0);
|
||||
Placement.placeBlock(core.x, core.y, ProductionBlocks.core, 0, false, false);
|
||||
|
||||
if(!map.name.equals("tutorial")){
|
||||
setDefaultBlocks();
|
||||
@@ -233,7 +231,7 @@ public class World extends Module{
|
||||
public int getSeed(){
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
public void removeBlock(Tile tile){
|
||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||
tile.setBlock(Blocks.air);
|
||||
@@ -246,32 +244,6 @@ public class World extends Module{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void placeBlock(int x, int y, Block result, int rotation){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
//just in case
|
||||
if(tile == null) return;
|
||||
|
||||
tile.setBlock(result, rotation);
|
||||
|
||||
if(result.isMultiblock()){
|
||||
int offsetx = -(result.width-1)/2;
|
||||
int offsety = -(result.height-1)/2;
|
||||
|
||||
for(int dx = 0; dx < result.width; dx ++){
|
||||
for(int dy = 0; dy < result.height; dy ++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
if(!(worldx == x && worldy == y)){
|
||||
Tile toplace = world.tile(worldx, worldy);
|
||||
if(toplace != null)
|
||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity findTileTarget(float x, float y, Tile tile, float range, boolean damaged){
|
||||
Entity closest = null;
|
||||
|
||||
@@ -83,7 +83,7 @@ public class Player extends SyncEntity{
|
||||
|
||||
@Override
|
||||
public void onDeath(){
|
||||
remove();
|
||||
dead = true;
|
||||
if(Net.active()){
|
||||
NetEvents.handlePlayerDeath();
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class Player extends SyncEntity{
|
||||
|
||||
@Override
|
||||
public void drawSmooth(){
|
||||
if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || (dead && !isLocal)) return;
|
||||
if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || dead) return;
|
||||
boolean snap = snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate") && isLocal;
|
||||
|
||||
String part = isAndroid ? "ship" : "mech";
|
||||
|
||||
@@ -2,26 +2,16 @@ package io.anuke.mindustry.input;
|
||||
|
||||
import com.badlogic.gdx.InputAdapter;
|
||||
import com.badlogic.gdx.math.GridPoint2;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.graphics.Fx;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetEvents;
|
||||
import io.anuke.mindustry.resource.ItemStack;
|
||||
import io.anuke.mindustry.resource.Recipe;
|
||||
import io.anuke.mindustry.resource.Recipes;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Placement;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Graphics;
|
||||
import io.anuke.ucore.core.Sounds;
|
||||
import io.anuke.ucore.entities.Entities;
|
||||
import io.anuke.ucore.entities.SolidEntity;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -35,8 +25,6 @@ public abstract class InputHandler extends InputAdapter{
|
||||
public PlaceMode lastPlaceMode = placeMode;
|
||||
public PlaceMode lastBreakMode = breakMode;
|
||||
|
||||
private Rectangle rect = new Rectangle();
|
||||
|
||||
public abstract void update();
|
||||
public abstract float getCursorX();
|
||||
public abstract float getCursorY();
|
||||
@@ -88,40 +76,6 @@ public abstract class InputHandler extends InputAdapter{
|
||||
|
||||
public boolean validPlace(int x, int y, Block type){
|
||||
|
||||
for(int i = 0; i < world.getSpawns().size; i ++){
|
||||
SpawnPoint spawn = world.getSpawns().get(i);
|
||||
if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rect.setSize(type.width * tilesize, type.height * tilesize);
|
||||
Vector2 offset = type.getPlaceOffset();
|
||||
rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize);
|
||||
|
||||
synchronized (Entities.entityLock) {
|
||||
for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) {
|
||||
if (e == null) continue; //not sure why this happens?
|
||||
Rectangle rect = e.hitbox.getRect(e.x, e.y);
|
||||
|
||||
if (this.rect.overlaps(rect)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(type.solid || type.solidifes) {
|
||||
for (Player player : playerGroup.all()) {
|
||||
if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false;
|
||||
|
||||
if(!type.isMultiblock() && control.tutorial().active() &&
|
||||
control.tutorial().showBlock()){
|
||||
|
||||
@@ -136,40 +90,11 @@ public abstract class InputHandler extends InputAdapter{
|
||||
}else if(control.tutorial().active()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(type.isMultiblock()){
|
||||
int offsetx = -(type.width-1)/2;
|
||||
int offsety = -(type.height-1)/2;
|
||||
for(int dx = 0; dx < type.width; dx ++){
|
||||
for(int dy = 0; dy < type.height; dy ++){
|
||||
Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
|
||||
if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
if(tile.block() != type && (type.canReplace(tile.block()) || tile.block().alwaysReplace) && tile.block().isMultiblock() == type.isMultiblock()){
|
||||
return true;
|
||||
}
|
||||
return tile.block() == Blocks.air;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSpawnPoint(Tile tile){
|
||||
return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2;
|
||||
return Placement.validPlace(x, y, type);
|
||||
}
|
||||
|
||||
public boolean validBreak(int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null || tile.block() == ProductionBlocks.core) return false;
|
||||
|
||||
if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(control.tutorial().active()){
|
||||
|
||||
if(control.tutorial().showBlock()){
|
||||
@@ -186,102 +111,25 @@ public abstract class InputHandler extends InputAdapter{
|
||||
}
|
||||
}
|
||||
|
||||
return tile.breakable();
|
||||
return Placement.validBreak(x, y);
|
||||
}
|
||||
|
||||
public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||
|
||||
placeBlockInternal(x, y, result, rotation, effects, sound);
|
||||
if(!Net.client()) Placement.placeBlock(x, y, result, rotation, effects, sound);
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile != null) result.placed(tile);
|
||||
|
||||
if(Net.active() && result != ProductionBlocks.core){
|
||||
if(Net.active()){
|
||||
NetEvents.handlePlace(x, y, result, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public void placeBlockInternal(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
//just in case
|
||||
if(tile == null)
|
||||
return;
|
||||
|
||||
tile.setBlock(result, rotation);
|
||||
|
||||
if(result.isMultiblock()){
|
||||
int offsetx = -(result.width-1)/2;
|
||||
int offsety = -(result.height-1)/2;
|
||||
|
||||
for(int dx = 0; dx < result.width; dx ++){
|
||||
for(int dy = 0; dy < result.height; dy ++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
if(!(worldx == x && worldy == y)){
|
||||
Tile toplace = world.tile(worldx, worldy);
|
||||
if(toplace != null)
|
||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
||||
}
|
||||
|
||||
if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
||||
}
|
||||
|
||||
if(effects && sound) Sounds.play("place");
|
||||
}
|
||||
|
||||
public void breakBlock(int x, int y, boolean sound){
|
||||
breakBlockInternal(x, y, sound);
|
||||
if(!Net.client()) Placement.breakBlock(x, y, sound, sound);
|
||||
|
||||
if(Net.active()){
|
||||
NetEvents.handleBreak(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void breakBlockInternal(int x, int y, boolean sound){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null) return;
|
||||
|
||||
Block block = tile.isLinked() ? tile.getLinked().block() : tile.block();
|
||||
Recipe result = null;
|
||||
|
||||
for(Recipe recipe : Recipes.all()){
|
||||
if(recipe.result == block){
|
||||
result = recipe;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(result != null){
|
||||
for(ItemStack stack : result.requirements){
|
||||
state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount));
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.block().drops != null){
|
||||
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
||||
}
|
||||
|
||||
//Effects.shake(3f, 1f, player);
|
||||
if(sound) Sounds.play("break");
|
||||
|
||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||
tile.setBlock(Blocks.air);
|
||||
Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Tile target = tile.isLinked() ? tile.getLinked() : tile;
|
||||
Array<Tile> removals = target.getLinkedTiles();
|
||||
for(Tile toremove : removals){
|
||||
//note that setting a new block automatically unlinks it
|
||||
toremove.setBlock(Blocks.air);
|
||||
Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +75,14 @@ public class Maps implements Disposable{
|
||||
}
|
||||
|
||||
public void loadMaps(){
|
||||
if(!loadMapFile(Gdx.files.internal("maps/maps.json"))){
|
||||
if(!loadMapFile(Gdx.files.internal("maps/maps.json"), true)){
|
||||
throw new RuntimeException("Failed to load maps!");
|
||||
}
|
||||
|
||||
if(!gwt) {
|
||||
if (!loadMapFile(customMapDirectory.child("maps.json"))) {
|
||||
if (!loadMapFile(customMapDirectory.child("maps.json"), false)) {
|
||||
try {
|
||||
Log.info("Failed to find custom map directory. Creating one instead.");
|
||||
customMapDirectory.child("maps.json").writeString("{}", false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create custom map directory!");
|
||||
@@ -159,7 +160,7 @@ public class Maps implements Disposable{
|
||||
saveMaps(out, customMapDirectory.child("maps.json"));
|
||||
}
|
||||
|
||||
private boolean loadMapFile(FileHandle file){
|
||||
private boolean loadMapFile(FileHandle file, boolean logException){
|
||||
try{
|
||||
Array<Map> arr = json.fromJson(ArrayContainer.class, file).maps;
|
||||
if(arr != null){ //can be an empty map file
|
||||
@@ -176,8 +177,10 @@ public class Maps implements Disposable{
|
||||
}
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
Log.err("Failed loading map file: {0}", file);
|
||||
if(logException) {
|
||||
Log.err(e);
|
||||
Log.err("Failed loading map file: {0}", file);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import io.anuke.ucore.core.Settings;
|
||||
|
||||
public class Administration {
|
||||
private Json json = new Json();
|
||||
private Array<String> bannedIPS = new Array<>();
|
||||
private Array<String> admins = new Array<>();
|
||||
private ObjectMap<String, String> known = new ObjectMap<>();
|
||||
|
||||
public Administration(){
|
||||
Settings.defaultList(
|
||||
"bans", "{}",
|
||||
"admins", "{}",
|
||||
"knownIPs", "{}"
|
||||
);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
/**Sets last known name for an IP.*/
|
||||
public void setKnownName(String ip, String name){
|
||||
known.put(ip, name);
|
||||
saveKnown();
|
||||
}
|
||||
|
||||
/**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/
|
||||
public String getLastName(String ip){
|
||||
return known.get(ip, "unknown");
|
||||
}
|
||||
|
||||
/**Returns list of banned IPs.*/
|
||||
public Array<String> getBanned(){
|
||||
return bannedIPS;
|
||||
}
|
||||
|
||||
/**Bans a player by IP; returns whether this player was already banned.*/
|
||||
public boolean banPlayer(String ip){
|
||||
if(bannedIPS.contains(ip, false))
|
||||
return false;
|
||||
bannedIPS.add(ip);
|
||||
saveBans();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Unbans a player by IP; returns whether this player was banned in the first place..*/
|
||||
public boolean unbanPlayer(String ip){
|
||||
if(!bannedIPS.contains(ip, false))
|
||||
return false;
|
||||
bannedIPS.removeValue(ip, false);
|
||||
saveBans();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Returns list of banned IPs.*/
|
||||
public Array<String> getAdmins(){
|
||||
return admins;
|
||||
}
|
||||
|
||||
/**Makes a player an admin. Returns whether this player was already an admin.*/
|
||||
public boolean adminPlayer(String ip){
|
||||
if(admins.contains(ip, false))
|
||||
return false;
|
||||
admins.add(ip);
|
||||
saveAdmins();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/
|
||||
public boolean unAdminPlayer(String ip){
|
||||
if(!admins.contains(ip, false))
|
||||
return false;
|
||||
admins.removeValue(ip, false);
|
||||
saveAdmins();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isBanned(String ip){
|
||||
return bannedIPS.contains(ip, false);
|
||||
}
|
||||
|
||||
public boolean isAdmin(String ip){
|
||||
return admins.contains(ip, false);
|
||||
}
|
||||
|
||||
private void saveKnown(){
|
||||
Settings.putString("knownIPs", json.toJson(known));
|
||||
Settings.save();
|
||||
}
|
||||
|
||||
private void saveBans(){
|
||||
Settings.putString("bans", json.toJson(bannedIPS));
|
||||
Settings.save();
|
||||
}
|
||||
|
||||
private void saveAdmins(){
|
||||
Settings.putString("admins", json.toJson(admins));
|
||||
Settings.save();
|
||||
}
|
||||
|
||||
private void load(){
|
||||
bannedIPS = json.fromJson(Array.class, Settings.getString("bans"));
|
||||
admins = json.fromJson(Array.class, Settings.getString("admins"));
|
||||
known = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,11 +3,15 @@ package io.anuke.mindustry.net;
|
||||
public class Host {
|
||||
public final String name;
|
||||
public final String address;
|
||||
public final String mapname;
|
||||
public final int wave;
|
||||
public final int players;
|
||||
|
||||
public Host(String name, String address, int players){
|
||||
public Host(String name, String address, String mapname, int wave, int players){
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.players = players;
|
||||
this.mapname = mapname;
|
||||
this.wave = wave;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.entities.Entity;
|
||||
|
||||
import static io.anuke.mindustry.Vars.netCommon;
|
||||
import static io.anuke.mindustry.Vars.ui;
|
||||
|
||||
public class NetEvents {
|
||||
|
||||
@@ -99,8 +98,6 @@ public class NetEvents {
|
||||
packet.name = Vars.player.name;
|
||||
packet.id = Vars.player.id;
|
||||
Net.send(packet, SendMode.tcp);
|
||||
|
||||
ui.chatfrag.addMessage(packet.text, netCommon.colorizeName(Vars.player.id, Vars.player.name));
|
||||
}
|
||||
|
||||
public static void handleShoot(Weapon weapon, float x, float y, float angle){
|
||||
|
||||
@@ -371,7 +371,7 @@ public class Packets {
|
||||
}
|
||||
|
||||
public enum KickReason{
|
||||
kick, invalidPassword, clientOutdated, serverOutdated
|
||||
kick, invalidPassword, clientOutdated, serverOutdated, banned
|
||||
}
|
||||
|
||||
public static class UpgradePacket implements Packet{
|
||||
@@ -558,4 +558,22 @@ public class Packets {
|
||||
itemid = buffer.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetErrorPacket implements Packet{
|
||||
public String message;
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer buffer) {
|
||||
buffer.putShort((short)message.getBytes().length);
|
||||
buffer.put(message.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer buffer) {
|
||||
short length = buffer.getShort();
|
||||
byte[] bytes = new byte[length];
|
||||
buffer.get(bytes);
|
||||
message = new String(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ public class Registrator {
|
||||
EntitySpawnPacket.class,
|
||||
ItemTransferPacket.class,
|
||||
ItemSetPacket.class,
|
||||
ItemOffloadPacket.class
|
||||
ItemOffloadPacket.class,
|
||||
NetErrorPacket.class,
|
||||
};
|
||||
private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||
import io.anuke.ucore.scene.ui.layout.Table;
|
||||
|
||||
import static io.anuke.mindustry.Vars.netServer;
|
||||
import static io.anuke.mindustry.Vars.ui;
|
||||
|
||||
public class AdminsDialog extends FloatingDialog {
|
||||
|
||||
public AdminsDialog(){
|
||||
super("$text.server.admins");
|
||||
|
||||
addCloseButton();
|
||||
|
||||
setup();
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
content().clear();
|
||||
|
||||
float w = 400f, h = 80f;
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
ScrollPane pane = new ScrollPane(table, "clear");
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
if(netServer.admins.getAdmins().size == 0){
|
||||
table.add("$text.server.admins.none");
|
||||
}
|
||||
|
||||
for(String ip : netServer.admins.getAdmins()){
|
||||
Table res = new Table("button");
|
||||
res.margin(14f);
|
||||
|
||||
res.labelWrap("[LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f);
|
||||
res.add().growX();
|
||||
res.addImageButton("icon-cancel", 14*3, () -> {
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||
netServer.admins.unAdminPlayer(ip);
|
||||
setup();
|
||||
});
|
||||
}).size(h).pad(-14f);
|
||||
|
||||
table.add(res).width(w).height(h);
|
||||
table.row();
|
||||
}
|
||||
|
||||
content().add(pane);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||
import io.anuke.ucore.scene.ui.layout.Table;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class BansDialog extends FloatingDialog {
|
||||
|
||||
public BansDialog(){
|
||||
super("$text.server.bans");
|
||||
|
||||
addCloseButton();
|
||||
|
||||
setup();
|
||||
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
content().clear();
|
||||
|
||||
float w = 400f, h = 80f;
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
ScrollPane pane = new ScrollPane(table, "clear");
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
if(netServer.admins.getBanned().size == 0){
|
||||
table.add("$text.server.bans.none");
|
||||
}
|
||||
|
||||
for(String ip : netServer.admins.getBanned()){
|
||||
Table res = new Table("button");
|
||||
res.margin(14f);
|
||||
|
||||
res.labelWrap("IP: [LIGHT_GRAY]" + ip + "\n[]Name: [LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f);
|
||||
res.add().growX();
|
||||
res.addImageButton("icon-cancel", 14*3, () -> {
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
|
||||
netServer.admins.unbanPlayer(ip);
|
||||
setup();
|
||||
});
|
||||
}).size(h).pad(-14f);
|
||||
|
||||
table.add(res).width(w).height(h);
|
||||
table.row();
|
||||
}
|
||||
|
||||
content().add(pane);
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,13 @@ public class HostDialog extends FloatingDialog{
|
||||
Settings.put("name", text);
|
||||
Settings.save();
|
||||
ui.listfrag.rebuild();
|
||||
}).grow().pad(8);
|
||||
}).grow().pad(8).get().setMaxLength(48);
|
||||
|
||||
ImageButton button = t.addImageButton("white", 40, () -> {
|
||||
new ColorPickDialog().show(color -> {
|
||||
player.color.set(color);
|
||||
Settings.putInt("color", Color.rgba8888(color));
|
||||
Settings.save();;
|
||||
Settings.save();
|
||||
});
|
||||
}).size(50f, 54f).get();
|
||||
button.update(() -> button.getStyle().imageUpColor = player.getColor());
|
||||
|
||||
@@ -85,7 +85,7 @@ public class JoinDialog extends FloatingDialog {
|
||||
|
||||
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
|
||||
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
|
||||
}).width(w).height(120f).pad(4f).get();
|
||||
}).width(w).height(140f).pad(4f).get();
|
||||
|
||||
button.getLabel().setWrap(true);
|
||||
|
||||
@@ -134,10 +134,14 @@ public class JoinDialog extends FloatingDialog {
|
||||
Net.pingHost(server.ip, server.port, host -> {
|
||||
server.content.clear();
|
||||
|
||||
server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).pad(4);
|
||||
server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left();
|
||||
server.content.row();
|
||||
server.content.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
|
||||
Bundles.format("text.players.single", host.players)));
|
||||
Bundles.format("text.players.single", host.players))).left();
|
||||
server.content.row();
|
||||
server.content.add("[lightgray]" + Bundles.format("text.save.map", host.mapname)).left();
|
||||
server.content.row();
|
||||
server.content.add("[lightgray]" + Bundles.format("text.save.wave", host.wave)).left();
|
||||
}, e -> {
|
||||
server.content.clear();
|
||||
server.content.add("$text.host.invalid");
|
||||
@@ -175,7 +179,7 @@ public class JoinDialog extends FloatingDialog {
|
||||
Vars.player.name = text;
|
||||
Settings.put("name", text);
|
||||
Settings.save();
|
||||
}).grow().pad(8);
|
||||
}).grow().pad(8).get().setMaxLength(48);
|
||||
|
||||
ImageButton button = t.addImageButton("white", 40, () -> {
|
||||
new ColorPickDialog().show(color -> {
|
||||
|
||||
@@ -3,12 +3,14 @@ package io.anuke.mindustry.ui.fragments;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.net.NetEvents;
|
||||
import io.anuke.mindustry.net.Packets.KickReason;
|
||||
import io.anuke.mindustry.ui.BorderImage;
|
||||
import io.anuke.ucore.core.Inputs;
|
||||
import io.anuke.ucore.graphics.Draw;
|
||||
import io.anuke.ucore.scene.Element;
|
||||
import io.anuke.ucore.scene.builders.button;
|
||||
import io.anuke.ucore.scene.builders.label;
|
||||
import io.anuke.ucore.scene.builders.table;
|
||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||
@@ -40,10 +42,20 @@ public class PlayerListFragment implements Fragment{
|
||||
row();
|
||||
new table("pane"){{
|
||||
margin(12f);
|
||||
|
||||
get().addCheck("$text.server.friendlyfire", b -> {
|
||||
state.friendlyFire = b;
|
||||
NetEvents.handleFriendlyFireChange(b);
|
||||
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client());
|
||||
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5);
|
||||
|
||||
new button("$text.server.bans", () -> {
|
||||
ui.bans.show();
|
||||
}).padTop(-12).padBottom(-12).fillY().cell.disabled(b -> Net.client());
|
||||
|
||||
new button("$text.server.admins", () -> {
|
||||
ui.admins.show();
|
||||
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client());
|
||||
|
||||
}}.pad(10f).growX().end();
|
||||
}}.end();
|
||||
|
||||
@@ -69,9 +81,13 @@ public class PlayerListFragment implements Fragment{
|
||||
public void rebuild(){
|
||||
content.clear();
|
||||
|
||||
float h = 60f;
|
||||
float h = 74f;
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
NetConnection connection = Net.getConnection(player.clientid);
|
||||
|
||||
if(connection == null && !player.isLocal) continue;
|
||||
|
||||
Table button = new Table("button");
|
||||
button.left();
|
||||
button.margin(5).marginBottom(10);
|
||||
@@ -93,19 +109,50 @@ public class PlayerListFragment implements Fragment{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
button.add(stack).size(h);
|
||||
button.add("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).pad(10);
|
||||
button.labelWrap("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).width(170f).pad(10);
|
||||
button.add().grow();
|
||||
|
||||
if(Net.server() && !player.isLocal){
|
||||
button.add().growY();
|
||||
button.addImageButton("icon-cancel", 14*3, () ->
|
||||
Net.kickConnection(player.clientid, KickReason.kick)
|
||||
).pad(-5).padBottom(-10).size(h+10, h+14);
|
||||
|
||||
float bs = (h + 14)/2f;
|
||||
|
||||
button.table(t -> {
|
||||
t.defaults().size(bs - 1, bs + 3);
|
||||
|
||||
t.addImageButton("icon-ban", 14*2, () -> {
|
||||
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
|
||||
netServer.admins.banPlayer(connection.address);
|
||||
Net.kickConnection(player.clientid, KickReason.banned);
|
||||
});
|
||||
}).padBottom(-5.1f);
|
||||
|
||||
t.addImageButton("icon-cancel", 14*2, () -> Net.kickConnection(player.clientid, KickReason.kick)).padBottom(-5.1f);
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageButton("icon-admin", "toggle", 14*2, () -> {
|
||||
if(netServer.admins.isAdmin(connection.address)){
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||
netServer.admins.unAdminPlayer(connection.address);
|
||||
//TODO send aproppriate packets.
|
||||
});
|
||||
}else{
|
||||
ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> {
|
||||
netServer.admins.adminPlayer(connection.address);
|
||||
//TODO send aproppriate packets.
|
||||
});
|
||||
}
|
||||
}).update(b -> b.setChecked(netServer.admins.isAdmin(connection.address)));
|
||||
|
||||
t.addImageButton("icon-cancel", 14*2, () -> Net.kickConnection(player.clientid, KickReason.kick));
|
||||
}).padRight(12).padTop(-5).padLeft(0).padBottom(-10).size(bs + 10f, bs);
|
||||
|
||||
|
||||
}
|
||||
|
||||
content.add(button).padBottom(-5).width(350f);
|
||||
content.add(button).padBottom(-6).width(350f).maxHeight(h + 14);
|
||||
content.row();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package io.anuke.mindustry.world;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.graphics.Fx;
|
||||
import io.anuke.mindustry.resource.ItemStack;
|
||||
import io.anuke.mindustry.resource.Recipe;
|
||||
import io.anuke.mindustry.resource.Recipes;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Sounds;
|
||||
import io.anuke.ucore.entities.Entities;
|
||||
import io.anuke.ucore.entities.SolidEntity;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Placement {
|
||||
private static final Rectangle rect = new Rectangle();
|
||||
|
||||
public static void breakBlock(int x, int y, boolean effect, boolean sound){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null) return;
|
||||
|
||||
Block block = tile.isLinked() ? tile.getLinked().block() : tile.block();
|
||||
Recipe result = null;
|
||||
|
||||
for(Recipe recipe : Recipes.all()){
|
||||
if(recipe.result == block){
|
||||
result = recipe;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(result != null){
|
||||
for(ItemStack stack : result.requirements){
|
||||
state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount));
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.block().drops != null){
|
||||
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
||||
}
|
||||
|
||||
if(sound) Sounds.play("break");
|
||||
|
||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||
tile.setBlock(Blocks.air);
|
||||
if(effect) Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Tile target = tile.isLinked() ? tile.getLinked() : tile;
|
||||
Array<Tile> removals = target.getLinkedTiles();
|
||||
for(Tile toremove : removals){
|
||||
//note that setting a new block automatically unlinks it
|
||||
toremove.setBlock(Blocks.air);
|
||||
if(effect) Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
//just in case
|
||||
if(tile == null) return;
|
||||
|
||||
tile.setBlock(result, rotation);
|
||||
|
||||
if(result.isMultiblock()){
|
||||
int offsetx = -(result.width-1)/2;
|
||||
int offsety = -(result.height-1)/2;
|
||||
|
||||
for(int dx = 0; dx < result.width; dx ++){
|
||||
for(int dy = 0; dy < result.height; dy ++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
if(!(worldx == x && worldy == y)){
|
||||
Tile toplace = world.tile(worldx, worldy);
|
||||
if(toplace != null)
|
||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
||||
}
|
||||
|
||||
if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize);
|
||||
}
|
||||
}
|
||||
}else if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
||||
|
||||
if(effects && sound) Sounds.play("place");
|
||||
}
|
||||
|
||||
public static boolean validPlace(int x, int y, Block type){
|
||||
for(int i = 0; i < world.getSpawns().size; i ++){
|
||||
SpawnPoint spawn = world.getSpawns().get(i);
|
||||
if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rect.setSize(type.width * tilesize, type.height * tilesize);
|
||||
Vector2 offset = type.getPlaceOffset();
|
||||
rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize);
|
||||
|
||||
synchronized (Entities.entityLock) {
|
||||
for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) {
|
||||
if (e == null) continue; //not sure why this happens?
|
||||
Rectangle rect = e.hitbox.getRect(e.x, e.y);
|
||||
|
||||
if (Placement.rect.overlaps(rect)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(type.solid || type.solidifes) {
|
||||
for (Player player : playerGroup.all()) {
|
||||
if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false;
|
||||
|
||||
if(type.isMultiblock()){
|
||||
int offsetx = -(type.width-1)/2;
|
||||
int offsety = -(type.height-1)/2;
|
||||
for(int dx = 0; dx < type.width; dx ++){
|
||||
for(int dy = 0; dy < type.height; dy ++){
|
||||
Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
|
||||
if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}else {
|
||||
return tile.block() != type
|
||||
&& (type.canReplace(tile.block()) || tile.block().alwaysReplace)
|
||||
&& tile.block().isMultiblock() == type.isMultiblock() || tile.block() == Blocks.air;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSpawnPoint(Tile tile){
|
||||
return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2;
|
||||
}
|
||||
|
||||
public static boolean validBreak(int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile == null || tile.block() == ProductionBlocks.core) return false;
|
||||
|
||||
if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){
|
||||
return false;
|
||||
}
|
||||
|
||||
return tile.breakable();
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class WebsocketClient implements ClientProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<IOException> failed) {
|
||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed) {
|
||||
if(!Platform.instance.canJoinGame()) {
|
||||
failed.accept(new IOException());
|
||||
}else {
|
||||
@@ -119,7 +119,7 @@ public class WebsocketClient implements ClientProvider {
|
||||
public void onMessage(String msg) {
|
||||
if(!msg.startsWith("---")) return;
|
||||
String[] text = msg.substring(3).split("\\|");
|
||||
Host host = new Host(text[1], address, Strings.parseInt(text[0]));
|
||||
Host host = new Host(text[1], address, text[2], Strings.parseInt(text[3]), Strings.parseInt(text[0]));
|
||||
valid.accept(host);
|
||||
accepted[0] = true;
|
||||
socket.close();
|
||||
|
||||
@@ -35,7 +35,7 @@ public class KryoClient implements ClientProvider{
|
||||
handler = new ClientDiscoveryHandler() {
|
||||
@Override
|
||||
public DatagramPacket onRequestNewDatagramPacket() {
|
||||
return new DatagramPacket(new byte[32], 32);
|
||||
return new DatagramPacket(new byte[128], 128);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.anuke.kryonet;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.esotericsoftware.minlog.Log.Logger;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.net.Host;
|
||||
import io.anuke.ucore.util.ColorCodes;
|
||||
|
||||
@@ -11,8 +10,7 @@ import java.io.StringWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
import static io.anuke.mindustry.Vars.playerGroup;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class KryoRegistrator {
|
||||
public static boolean fakeLag = false;
|
||||
@@ -49,24 +47,44 @@ public class KryoRegistrator {
|
||||
}
|
||||
|
||||
public static ByteBuffer writeServerData(){
|
||||
String host = headless ? "Server" : Vars.player.name;
|
||||
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);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1 + host.getBytes().length + 4);
|
||||
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){
|
||||
//old version address.
|
||||
if(buffer.capacity() == 4) return null;
|
||||
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);
|
||||
|
||||
byte length = buffer.get();
|
||||
byte[] sname = new byte[length];
|
||||
buffer.get(sname);
|
||||
int players = buffer.getInt();
|
||||
int wave = buffer.getInt();
|
||||
|
||||
return new Host(new String(sname), ia.getHostAddress(), players);
|
||||
return new Host(host, ia.getHostAddress(), map, wave, players);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,7 @@ import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.Net.ServerProvider;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.net.Packets.Connect;
|
||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
||||
import io.anuke.mindustry.net.Packets.KickPacket;
|
||||
import io.anuke.mindustry.net.Packets.KickReason;
|
||||
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;
|
||||
@@ -25,6 +22,7 @@ import io.anuke.mindustry.net.Streamable.StreamChunk;
|
||||
import io.anuke.ucore.UCore;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.util.Log;
|
||||
import io.anuke.ucore.util.Strings;
|
||||
import org.java_websocket.WebSocket;
|
||||
import org.java_websocket.exceptions.WebsocketNotConnectedException;
|
||||
import org.java_websocket.handshake.ClientHandshake;
|
||||
@@ -66,13 +64,15 @@ public class KryoServer implements ServerProvider {
|
||||
|
||||
@Override
|
||||
public void connected (Connection connection) {
|
||||
KryoConnection kn = new KryoConnection(lastconnection ++, connection.getRemoteAddressTCP().toString(), connection);
|
||||
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
||||
|
||||
KryoConnection kn = new KryoConnection(lastconnection ++, ip, connection);
|
||||
|
||||
Connect c = new Connect();
|
||||
c.id = kn.id;
|
||||
c.addressTCP = connection.getRemoteAddressTCP().toString();
|
||||
c.addressTCP = ip;
|
||||
|
||||
Log.info("&bRecieved connection: {0} {1}", c.id, c.addressTCP);
|
||||
Log.info("&bRecieved connection: {0} / {1}", c.id, c.addressTCP);
|
||||
|
||||
connections.add(kn);
|
||||
Gdx.app.postRunnable(() -> Net.handleServerReceived(kn.id, c));
|
||||
@@ -141,12 +141,15 @@ public class KryoServer implements ServerProvider {
|
||||
if(con == null){
|
||||
Log.err("Cannot kick unknown player!");
|
||||
return;
|
||||
}else{
|
||||
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason);
|
||||
}
|
||||
|
||||
KickPacket p = new KickPacket();
|
||||
p.reason = reason;
|
||||
|
||||
con.send(p, SendMode.tcp);
|
||||
Timers.runTask(2f, con::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -371,9 +374,21 @@ public class KryoServer implements ServerProvider {
|
||||
connection.sendUDP(object);
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
Log.err(e);
|
||||
Log.info("Disconnecting invalid client!");
|
||||
try{
|
||||
NetErrorPacket packet = new NetErrorPacket();
|
||||
packet.message = Strings.parseException(e, true);
|
||||
Timers.runTask(5f, connection::close);
|
||||
}catch (Exception e2){
|
||||
Log.err(e2);
|
||||
connection.close();
|
||||
}
|
||||
connection.close();
|
||||
|
||||
KryoConnection k = getByKryoID(connection.getID());
|
||||
if(k != null) connections.remove(k);
|
||||
Log.info("Connection removed {0}", k);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,6 +404,8 @@ public class KryoServer implements ServerProvider {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SocketServer extends WebSocketServer {
|
||||
|
||||
public SocketServer(int port) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -38,7 +38,12 @@ public class ServerControl extends Module {
|
||||
private ShuffleMode mode;
|
||||
|
||||
public ServerControl(){
|
||||
Settings.defaults("shufflemode", "normal");
|
||||
Settings.defaultList(
|
||||
"shufflemode", "normal",
|
||||
"bans", "",
|
||||
"admins", ""
|
||||
);
|
||||
|
||||
mode = ShuffleMode.valueOf(Settings.getString("shufflemode"));
|
||||
|
||||
Effects.setScreenShakeProvider((a, b) -> {});
|
||||
@@ -184,6 +189,21 @@ public class ServerControl extends Module {
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("players", "Display player info.", arg -> {
|
||||
if(state.is(State.menu)){
|
||||
info("&lyServer is closed.");
|
||||
}else{
|
||||
if(playerGroup.size() > 0) {
|
||||
info("&lyPlayers: {0}", playerGroup.size());
|
||||
for (Player p : playerGroup.all()) {
|
||||
print(" &y{0} / Connection {1} / IP: {2}", p.name, p.clientid, Net.getConnection(p.clientid).address);
|
||||
}
|
||||
}else{
|
||||
info("&lyNo players connected.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("say", "<message...>", "Send a message to all players.", arg -> {
|
||||
if(!state.is(State.playing)) {
|
||||
err("Not hosting. Host a game first.");
|
||||
@@ -237,11 +257,6 @@ public class ServerControl extends Module {
|
||||
return;
|
||||
}
|
||||
|
||||
if(playerGroup.size() == 0){
|
||||
err("But this server is empty. A barren wasteland.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = null;
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
@@ -259,6 +274,130 @@ public class ServerControl extends Module {
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("ban", "<username>", "Ban a person by name.", arg -> {
|
||||
if(!state.is(State.playing)) {
|
||||
err("Can't ban people by name with no players.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = null;
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.equalsIgnoreCase(arg[0])){
|
||||
target = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
String ip = Net.getConnection(player.clientid).address;
|
||||
netServer.admins.banPlayer(ip);
|
||||
Net.kickConnection(target.clientid, KickReason.banned);
|
||||
info("Banned player by IP: {0}", ip);
|
||||
}else{
|
||||
info("Nobody with that name could be found.");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("bans", "List all banned IPs.", arg -> {
|
||||
Array<String> bans = netServer.admins.getBanned();
|
||||
|
||||
if(bans.size == 0){
|
||||
Log.info("No banned players have been found.");
|
||||
}else{
|
||||
Log.info("&lyBanned players:");
|
||||
for(String string : bans){
|
||||
Log.info(" &luy {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("banip", "<ip>", "Ban a person by IP.", arg -> {
|
||||
if(netServer.admins.banPlayer(arg[0])) {
|
||||
info("Banned player by IP: {0}.", arg[0]);
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
if(Net.getConnection(player.clientid).address.equals(arg[0])){
|
||||
Net.kickConnection(player.clientid, KickReason.banned);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
err("That IP is already banned!");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("unbanip", "<ip>", "Unban a person by IP.", arg -> {
|
||||
if(netServer.admins.unbanPlayer(arg[0])) {
|
||||
info("Unbanned player by IP: {0}.", arg[0]);
|
||||
}else{
|
||||
err("That IP is not banned!");
|
||||
}
|
||||
});
|
||||
|
||||
//assadsad
|
||||
|
||||
handler.register("admin", "<username>", "Make a user admin", arg -> {
|
||||
if(!state.is(State.playing)) {
|
||||
err("Open the server first.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = null;
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.equalsIgnoreCase(arg[0])){
|
||||
target = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
String ip = Net.getConnection(player.clientid).address;
|
||||
netServer.admins.adminPlayer(ip);
|
||||
info("Admin-ed player by IP: {0} / {1}", ip, arg[0]);
|
||||
}else{
|
||||
info("Nobody with that name could be found.");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("unadmin", "<username>", "Removes admin status from a player", arg -> {
|
||||
if(!state.is(State.playing)) {
|
||||
err("Open the server first.");
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = null;
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.equalsIgnoreCase(arg[0])){
|
||||
target = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
String ip = Net.getConnection(player.clientid).address;
|
||||
netServer.admins.unAdminPlayer(ip);
|
||||
info("Un-admin-ed player by IP: {0} / {1}", ip, arg[0]);
|
||||
}else{
|
||||
info("Nobody with that name could be found.");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("admins", "List all banned IPs.", arg -> {
|
||||
Array<String> admins = netServer.admins.getAdmins();
|
||||
|
||||
if(admins.size == 0){
|
||||
Log.info("No admins have been found.");
|
||||
}else{
|
||||
Log.info("&lyAdmins:");
|
||||
for(String string : admins){
|
||||
Log.info(" &luy {0} / Name: '{1}'", string, netServer.admins.getLastName(string));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("runwave", "Trigger the next wave.", arg -> {
|
||||
if(!state.is(State.playing)) {
|
||||
err("Not hosting. Host a game first.");
|
||||
|
||||
Reference in New Issue
Block a user