Merge branch 'master' into crater

# Conflicts:
#	core/assets/sprites/block_colors.png
#	core/assets/sprites/sprites.atlas
#	core/assets/sprites/sprites.png
#	core/assets/sprites/sprites3.png
#	core/assets/sprites/sprites5.png
This commit is contained in:
Patrick 'Quezler' Mounier
2020-01-10 12:02:07 +01:00
73 changed files with 6154 additions and 4205 deletions

View File

@@ -49,7 +49,7 @@ public class Blocks implements ContentList{
melter, separator, sporePress, pulverizer, incinerator, coalCentrifuge,
//sandbox
powerSource, powerVoid, itemSource, itemVoid, liquidSource, message, illuminator,
powerSource, powerVoid, itemSource, itemVoid, liquidSource, liquidVoid, message, illuminator,
//defense
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
@@ -1823,6 +1823,11 @@ public class Blocks implements ContentList{
alwaysUnlocked = true;
}};
liquidVoid = new LiquidVoid("liquid-void"){{
requirements(Category.liquid, BuildVisibility.sandboxOnly, ItemStack.with());
alwaysUnlocked = true;
}};
message = new MessageBlock("message"){{
requirements(Category.effect, ItemStack.with(Items.graphite, 5));
}};

View File

@@ -1,9 +1,10 @@
package mindustry.content;
import arc.struct.Array;
import mindustry.ctype.ContentList;
import mindustry.type.ItemStack;
import mindustry.world.Block;
import arc.math.*;
import arc.struct.*;
import mindustry.ctype.*;
import mindustry.type.*;
import mindustry.world.*;
import static mindustry.content.Blocks.*;
@@ -322,7 +323,7 @@ public class TechTree implements ContentList{
private static TechNode node(Block block, Runnable children){
ItemStack[] requirements = new ItemStack[block.requirements.length];
for(int i = 0; i < requirements.length; i++){
requirements[i] = new ItemStack(block.requirements[i].item, 30 + block.requirements[i].amount * 6);
requirements[i] = new ItemStack(block.requirements[i].item, 40 + Mathf.round(Mathf.pow(block.requirements[i].amount, 1.25f) * 6, 10));
}
return new TechNode(block, requirements, children);

View File

@@ -47,8 +47,8 @@ public class Logic implements ApplicationListener{
//blocks that get broken are appended to the team's broken block queue
Tile tile = event.tile;
Block block = tile.block();
//skip null entities or nukes, for obvious reasons; also skip client since they can't modify these requests
if(tile.entity == null || tile.block() instanceof NuclearReactor || net.client()) return;
//skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests
if(tile.entity == null || !tile.block().rebuildable || net.client()) return;
if(block instanceof BuildBlock){

View File

@@ -160,7 +160,7 @@ public class NetClient implements ApplicationListener{
throw new ValidateException(player, "Player has sent a message above the text limit.");
}
String original = message;
Events.fire(new PlayerChatEvent(player, message));
//check if it's a command
CommandResponse response = netServer.clientCommands.handleMessage(message, player);
@@ -197,8 +197,6 @@ public class NetClient implements ApplicationListener{
player.sendMessage(text);
}
}
Events.fire(new PlayerChatEvent(player, message, original));
}
public static String colorizeName(int id, String name){

View File

@@ -15,6 +15,7 @@ import mindustry.entities.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.traits.*;
import mindustry.entities.type.*;
import mindustry.net.Administration;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.game.Teams.*;
@@ -35,7 +36,7 @@ import static mindustry.Vars.*;
public class NetServer implements ApplicationListener{
private final static int maxSnapshotSize = 430, timerBlockSync = 0;
private final static float serverSyncTime = 12, kickDuration = 30 * 1000, blockSyncTime = 60 * 8;
private final static float serverSyncTime = 12, blockSyncTime = 60 * 8;
private final static Vec2 vector = new Vec2();
private final static Rect viewport = new Rect();
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
@@ -75,7 +76,7 @@ public class NetServer implements ApplicationListener{
public NetServer(){
net.handleServer(Connect.class, (con, connect) -> {
if(admins.isIPBanned(connect.addressTCP)){
if(admins.isIPBanned(connect.addressTCP) || admins.isSubnetBanned(connect.addressTCP)){
con.kick(KickReason.banned);
}
});
@@ -93,7 +94,7 @@ public class NetServer implements ApplicationListener{
String uuid = packet.uuid;
if(admins.isIPBanned(con.address)) return;
if(admins.isIPBanned(con.address) || admins.isSubnetBanned(con.address)) return;
if(con.hasBegunConnecting){
con.kick(KickReason.idInUse);
@@ -115,7 +116,7 @@ public class NetServer implements ApplicationListener{
return;
}
if(Time.millis() - info.lastKicked < kickDuration){
if(Time.millis() < info.lastKicked){
con.kick(KickReason.recentKick);
return;
}
@@ -413,6 +414,12 @@ public class NetServer implements ApplicationListener{
if(player.isLocal){
player.sendMessage("[scarlet]Re-synchronizing as the host is pointless.");
}else{
if(Time.timeSinceMillis(player.getInfo().lastSyncTime) < 1000 * 5){
player.sendMessage("[scarlet]You may only /sync every 5 seconds.");
return;
}
player.getInfo().lastSyncTime = Time.millis();
Call.onWorldDataBegin(player.con);
netServer.sendWorldData(player);
}

View File

@@ -118,7 +118,7 @@ public enum EditorTool{
if(editor.drawBlock.isOverlay()){
Block dest = tile.overlay();
if(dest == editor.drawBlock) return;
tester = t -> t.overlay() == dest;
tester = t -> t.overlay() == dest && !t.floor().isLiquid;
setter = t -> t.setOverlay(editor.drawBlock);
}else if(editor.drawBlock.isFloor()){
Block dest = tile.floor();

View File

@@ -55,7 +55,7 @@ public class Predict{
* See {@link #intercept(float, float, float, float, float, float, float)}.
*/
public static Vec2 intercept(TargetTrait src, TargetTrait dst, float v){
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/2f, dst.getTargetVelocityY() - src.getTargetVelocityY()/2f, v);
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/(2f*Time.delta()), dst.getTargetVelocityY() - src.getTargetVelocityY()/(2f*Time.delta()), v);
}
private static Vec2 quad(float a, float b, float c){

View File

@@ -48,8 +48,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public float baseRotation;
public float pointerX, pointerY;
public String name = "noname";
public @Nullable
String uuid, usid;
public @Nullable String uuid, usid;
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping, isBuilding = true;
public boolean buildWasAutoPaused = false;
public float boostHeat, shootHeat, destructTime;
@@ -350,13 +349,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
Draw.reset();
}
public void drawBackItems(){
drawBackItems(itemtime, isLocal);
}
@Override
public void drawStats(){
Draw.color(Color.black, team.color, healthf() + Mathf.absin(Time.time(), healthf() * 5f, 1f - healthf()));
Draw.rect(getPowerCellRegion(), x + Angles.trnsx(rotation, mech.cellTrnsY, 0f), y + Angles.trnsy(rotation, mech.cellTrnsY, 0f), rotation - 90);
Draw.reset();
drawBackItems(itemtime, isLocal);
drawLight();
mech.drawStats(this);
}
@Override

View File

@@ -252,6 +252,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
Tile tile = world.tileWorld(x, y);
status.update(this);
item.amount = Mathf.clamp(this.item.amount, 0, getItemCapacity());
velocity.limit(maxVelocity()).scl(1f + (status.getSpeedMultiplier() - 1f) * Time.delta());
@@ -341,6 +342,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void addItem(Item item, int amount){
this.item.amount = this.item.item == item ? this.item.amount + amount : amount;
this.item.item = item;
this.item.amount = Mathf.clamp(this.item.amount, 0, getItemCapacity());
}
public void clearItem(){

View File

@@ -64,13 +64,10 @@ public class EventType{
public static class PlayerChatEvent{
public final Player player;
public final String message;
/** The original, unfiltered message. */
public final String originalMessage;
public PlayerChatEvent(Player player, String message, String originalMessage){
public PlayerChatEvent(Player player, String message){
this.player = player;
this.message = message;
this.originalMessage = originalMessage;
}
}

View File

@@ -14,7 +14,6 @@ import arc.graphics.gl.Shader;
* @author kalle_h
*/
public class Bloom{
/**
* To use implement bloom more like a glow. Texture alpha channel can be
* used as mask which part are glowing and which are not. see more info at:
@@ -61,10 +60,8 @@ public class Bloom{
*/
public void resume(){
bloomShader.begin();
{
bloomShader.setUniformi("u_texture0", 0);
bloomShader.setUniformi("u_texture1", 1);
}
bloomShader.setUniformi("u_texture0", 0);
bloomShader.setUniformi("u_texture1", 1);
bloomShader.end();
setSize(w, h);
@@ -83,13 +80,11 @@ public class Bloom{
* blending = false 32bits = true
*/
public Bloom(){
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
null, false, false, true);
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, false, true);
}
public Bloom(boolean useBlending){
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
null, false, useBlending, true);
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, useBlending, true);
}
/**
@@ -105,8 +100,7 @@ public class Bloom{
* and only do blooming on certain objects param use32bitFBO does
* fbo use higher precision than 16bits.
*/
public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending,
boolean use32bitFBO){
public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
initialize(FBO_W, FBO_H, null, hasDepth, useBlending, use32bitFBO);
}
@@ -129,18 +123,14 @@ public class Bloom{
* and only do blooming on certain objects param use32bitFBO does
* fbo use higher precision than 16bits.
*/
public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere,
boolean useBlending, boolean use32bitFBO){
initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending,
use32bitFBO);
public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere, boolean useBlending, boolean use32bitFBO){
initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending, use32bitFBO);
disposeFBO = false;
}
private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo,
boolean hasDepth, boolean useBlending, boolean use32bitFBO){
private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
blending = useBlending;
Format format = null;
Format format;
if(use32bitFBO){
if(useBlending){
@@ -157,8 +147,7 @@ public class Bloom{
}
}
if(fbo == null){
frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(),
Core.graphics.getHeight(), hasDepth);
frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(), Core.graphics.getHeight(), hasDepth);
}else{
frameBuffer = fbo;
}
@@ -190,10 +179,8 @@ public class Bloom{
setThreshold(0.5f);
bloomShader.begin();
{
bloomShader.setUniformi("u_texture0", 0);
bloomShader.setUniformi("u_texture1", 1);
}
bloomShader.setUniformi("u_texture0", 0);
bloomShader.setUniformi("u_texture1", 1);
bloomShader.end();
}
@@ -219,8 +206,8 @@ public class Bloom{
if(!capturing){
capturing = true;
frameBuffer.begin();
Core.gl.glClearColor(r, g, b, a);
Core.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gl.clearColor(r, g, b, a);
Gl.clear(Gl.colorBufferBit);
}
}
@@ -252,23 +239,22 @@ public class Bloom{
frameBuffer.end();
}
Core.gl.glDisable(GL20.GL_BLEND);
Core.gl.glDisable(GL20.GL_DEPTH_TEST);
Core.gl.glDepthMask(false);
Gl.disable(Gl.blend);
Gl.disable(Gl.depthTest);
Gl.depthMask(false);
gaussianBlur();
if(blending){
Core.gl.glEnable(GL20.GL_BLEND);
Core.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gl.enable(Gl.blend);
Gl.blendFunc(Gl.srcAlpha, Gl.oneMinusSrcAlpha);
}
pingPongTex1.bind(1);
original.bind(0);
bloomShader.begin();
{
fullScreenQuad.render(bloomShader, GL20.GL_TRIANGLE_FAN);
}
fullScreenQuad.render(bloomShader, Gl.triangleFan);
bloomShader.end();
}
@@ -279,15 +265,9 @@ public class Bloom{
original.bind(0);
pingPongBuffer1.begin();
{
tresholdShader.begin();
{
// tresholdShader.setUniformi("u_texture0", 0);
fullScreenQuad.render(tresholdShader, GL20.GL_TRIANGLE_FAN, 0,
4);
}
tresholdShader.end();
}
tresholdShader.begin();
fullScreenQuad.render(tresholdShader, Gl.triangleFan, 0, 4);
tresholdShader.end();
pingPongBuffer1.end();
for(int i = 0; i < blurPasses; i++){
@@ -296,30 +276,19 @@ public class Bloom{
// horizontal
pingPongBuffer2.begin();
{
blurShader.begin();
{
blurShader.setUniformf("dir", 1f, 0f);
fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
4);
}
blurShader.end();
}
blurShader.begin();
blurShader.setUniformf("dir", 1f, 0f);
fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
blurShader.end();
pingPongBuffer2.end();
pingPongTex2.bind(0);
// vertical
pingPongBuffer1.begin();
{
blurShader.begin();
{
blurShader.setUniformf("dir", 0f, 1f);
fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
4);
}
blurShader.end();
}
blurShader.begin();
blurShader.setUniformf("dir", 0f, 1f);
fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
blurShader.end();
pingPongBuffer1.end();
}
}
@@ -334,9 +303,7 @@ public class Bloom{
public void setBloomIntesity(float intensity){
bloomIntensity = intensity;
bloomShader.begin();
{
bloomShader.setUniformf("BloomIntensity", intensity);
}
bloomShader.setUniformf("BloomIntensity", intensity);
bloomShader.end();
}
@@ -350,9 +317,7 @@ public class Bloom{
public void setOriginalIntesity(float intensity){
originalIntensity = intensity;
bloomShader.begin();
{
bloomShader.setUniformf("OriginalIntensity", intensity);
}
bloomShader.setUniformf("OriginalIntensity", intensity);
bloomShader.end();
}
@@ -364,10 +329,7 @@ public class Bloom{
public void setThreshold(float threshold){
this.threshold = threshold;
tresholdShader.begin();
{
tresholdShader.setUniformf("threshold", threshold,
1f / (1 - threshold));
}
tresholdShader.setUniformf("threshold", threshold, 1f / (1 - threshold));
tresholdShader.end();
}
@@ -384,8 +346,7 @@ public class Bloom{
*/
public void dispose(){
try{
if(disposeFBO)
frameBuffer.dispose();
if(disposeFBO) frameBuffer.dispose();
fullScreenQuad.dispose();
@@ -395,16 +356,17 @@ public class Bloom{
blurShader.dispose();
bloomShader.dispose();
tresholdShader.dispose();
}catch(Exception ignored){
}catch(Throwable ignored){
}
}
private static Mesh createFullScreenQuad(){
float[] verts = {-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1};
Mesh tmpMesh = new Mesh(true, 4, 0, new VertexAttribute(
Usage.Position, 2, "a_position"), new VertexAttribute(
Usage.TextureCoordinates, 2, "a_texCoord0"));
Mesh tmpMesh = new Mesh(true, 4, 0,
new VertexAttribute(Usage.position, 2, "a_position"),
new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0")
);
tmpMesh.setVertices(verts);
return tmpMesh;

View File

@@ -1,22 +1,19 @@
package mindustry.graphics;
import arc.Core;
import arc.Events;
import arc.struct.*;
import arc.struct.IntSet.IntSetIterator;
import arc.graphics.Camera;
import arc.graphics.GL20;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.Mathf;
import arc.math.*;
import arc.struct.*;
import arc.struct.IntSet.*;
import arc.util.*;
import mindustry.game.EventType.WorldLoadEvent;
import mindustry.world.Tile;
import mindustry.world.blocks.Floor;
import mindustry.game.EventType.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import java.util.Arrays;
import java.util.*;
import static mindustry.Vars.tilesize;
import static mindustry.Vars.world;
import static mindustry.Vars.*;
public class FloorRenderer implements Disposable{
private final static int chunksize = 64;
@@ -104,7 +101,7 @@ public class FloorRenderer implements Disposable{
cbatch.setProjection(Core.camera.projection());
cbatch.beginDraw();
Core.gl.glEnable(GL20.GL_BLEND);
Gl.enable(Gl.blend);
}
public void endDraw(){

View File

@@ -1,15 +1,11 @@
package mindustry.graphics;
import arc.Core;
import arc.graphics.*;
import arc.graphics.VertexAttributes.Usage;
import arc.graphics.g2d.BatchShader;
import arc.graphics.g2d.TextureRegion;
import arc.graphics.gl.Shader;
import arc.math.Mathf;
import arc.math.Matrix3;
import arc.util.Disposable;
import arc.util.Strings;
import arc.graphics.VertexAttributes.*;
import arc.graphics.g2d.*;
import arc.graphics.gl.*;
import arc.math.*;
import arc.util.*;
//TODO this class is a trainwreck, remove it
public class IndexedRenderer implements Disposable{
@@ -17,18 +13,18 @@ public class IndexedRenderer implements Disposable{
private Shader program = new Shader(
Strings.join("\n",
"attribute vec4 " + Shader.POSITION_ATTRIBUTE + ";",
"attribute vec4 " + Shader.COLOR_ATTRIBUTE + ";",
"attribute vec2 " + Shader.TEXCOORD_ATTRIBUTE + "0;",
"attribute vec4 " + Shader.positionAttribute + ";",
"attribute vec4 " + Shader.colorAttribute + ";",
"attribute vec2 " + Shader.texcoordAttribute + "0;",
"uniform mat4 u_projTrans;",
"varying vec4 v_color;",
"varying vec2 v_texCoords;",
"",
"void main(){",
" v_color = " + Shader.COLOR_ATTRIBUTE + ";",
" v_color = " + Shader.colorAttribute + ";",
" v_color.a = v_color.a * (255.0/254.0);",
" v_texCoords = " + Shader.TEXCOORD_ATTRIBUTE + "0;",
" gl_Position = u_projTrans * " + Shader.POSITION_ATTRIBUTE + ";",
" v_texCoords = " + Shader.texcoordAttribute + "0;",
" gl_Position = u_projTrans * " + Shader.positionAttribute + ";",
"}"),
Strings.join("\n",
"#ifdef GL_ES",
@@ -60,7 +56,7 @@ public class IndexedRenderer implements Disposable{
}
public void render(Texture texture){
Core.gl.glEnable(GL20.GL_BLEND);
Gl.enable(Gl.blend);
updateMatrix();
@@ -71,7 +67,7 @@ public class IndexedRenderer implements Disposable{
program.setUniformMatrix4("u_projTrans", BatchShader.copyTransform(combined));
program.setUniformi("u_texture", 0);
mesh.render(program, GL20.GL_TRIANGLES, 0, vertices.length / vsize);
mesh.render(program, Gl.triangles, 0, vertices.length / vsize);
program.end();
}
@@ -226,9 +222,9 @@ public class IndexedRenderer implements Disposable{
if(mesh != null) mesh.dispose();
mesh = new Mesh(true, 6 * sprites, 0,
new VertexAttribute(Usage.Position, 2, "a_position"),
new VertexAttribute(Usage.ColorPacked, 4, "a_color"),
new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0"));
new VertexAttribute(Usage.position, 2, "a_position"),
new VertexAttribute(Usage.colorPacked, 4, "a_color"),
new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0"));
vertices = new float[6 * sprites * vsize];
mesh.setVertices(vertices);
}

View File

@@ -1,11 +1,17 @@
package mindustry.net;
import arc.*;
import arc.func.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import arc.util.pooling.*;
import arc.util.pooling.Pool.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.entities.type.*;
import mindustry.type.*;
import mindustry.world.*;
import static mindustry.Vars.headless;
import static mindustry.game.EventType.*;
@@ -16,9 +22,61 @@ public class Administration{
private Array<String> bannedIPs = new Array<>();
private Array<String> whitelist = new Array<>();
private Array<ChatFilter> chatFilters = new Array<>();
private Array<ActionFilter> actionFilters = new Array<>();
private Array<String> subnetBans = new Array<>();
public Administration(){
load();
//anti-spam
addChatFilter((player, message) -> {
long resetTime = Config.messageRateLimit.num() * 1000;
if(Config.antiSpam.bool() && !player.isLocal && !player.isAdmin){
//prevent people from spamming messages quickly
if(resetTime > 0 && Time.timeSinceMillis(player.getInfo().lastMessageTime) < resetTime){
//supress message
player.sendMessage("[scarlet]You may only send messages every " + Config.messageRateLimit.num() + " seconds.");
player.getInfo().messageInfractions ++;
//kick player for spamming and prevent connection if they've done this several times
if(player.getInfo().messageInfractions >= Config.messageSpamKick.num() && Config.messageSpamKick.num() != 0){
player.con.kick("You have been kicked for spamming.", 1000 * 60 * 2);
}
player.getInfo().lastSentMessage = message;
return null;
}else{
player.getInfo().messageInfractions = 0;
}
//prevent players from sending the same message twice in the span of 50 seconds
if(message.equals(player.getInfo().lastSentMessage) && Time.timeSinceMillis(player.getInfo().lastMessageTime) < 1000 * 50){
player.sendMessage("[scarlet]You may not send the same message twice.");
return null;
}
player.getInfo().lastSentMessage = message;
player.getInfo().lastMessageTime = Time.millis();
}
return message;
});
}
public Array<String> getSubnetBans(){
return subnetBans;
}
public void removeSubnetBan(String ip){
subnetBans.remove(ip);
save();
}
public void addSubnetBan(String ip){
subnetBans.add(ip);
save();
}
public boolean isSubnetBanned(String ip){
return subnetBans.contains(ip::startsWith);
}
/** Adds a chat filter. This will transform the chat messages of every player.
@@ -38,6 +96,25 @@ public class Administration{
return current;
}
/** Add a filter to actions, preventing things such as breaking or configuring blocks. */
public void addActionFilter(ActionFilter filter){
actionFilters.add(filter);
}
/** @return whether this action is allowed by the action filters. */
public boolean allowAction(Player player, ActionType type, Tile tile, Cons<PlayerAction> setter){
PlayerAction act = Pools.obtain(PlayerAction.class, PlayerAction::new);
setter.get(act.set(player, type, tile));
for(ActionFilter filter : actionFilters){
if(!filter.allow(act)){
Pools.free(act);
return false;
}
}
Pools.free(act);
return true;
}
public int getPlayerLimit(){
return Core.settings.getInt("playerlimit", 0);
}
@@ -305,6 +382,7 @@ public class Administration{
Core.settings.putObject("player-info", playerInfo);
Core.settings.putObject("banned-ips", bannedIPs);
Core.settings.putObject("whitelisted", whitelist);
Core.settings.putObject("subnet-bans", subnetBans);
Core.settings.save();
}
@@ -313,6 +391,7 @@ public class Administration{
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);
subnetBans = Core.settings.getObject("subnet-bans", Array.class, Array::new);
}
/** Server configuration definition. Each config value can be a string, boolean or number. */
@@ -326,6 +405,9 @@ public class Administration{
crashReport("Whether to send crash reports.", false, "crashreport"),
logging("Whether to log everything to files.", true),
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
antiSpam("Whether spammers are automatically kicked and rate-limited.", true),
messageRateLimit("Message rate limit in seconds. 0 to disable.", 0),
messageSpamKick("How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3),
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
socketInputPort("The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
socketInputAddress("The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),
@@ -402,7 +484,11 @@ public class Administration{
public int timesKicked;
public int timesJoined;
public boolean banned, admin;
public long lastKicked; //last kicked timestamp
public long lastKicked; //last kicked time to expiration
public transient long lastMessageTime, lastSyncTime;
public transient String lastSentMessage;
public transient int messageInfractions;
PlayerInfo(String id){
this.id = id;
@@ -412,11 +498,18 @@ public class Administration{
}
}
/** Handles chat messages from players and changes their contents. */
public interface ChatFilter{
/** @return the filtered message; a null string signals that the message should not be sent. */
@Nullable String filter(Player player, String message);
}
/** Allows or disallows player actions. */
public interface ActionFilter{
/** @return whether this action should be permitted. if applicable, make sure to send this player a message specify why the action was prohibited. */
boolean allow(PlayerAction action);
}
public static class TraceInfo{
public String ip, uuid;
public boolean modded, mobile;
@@ -429,4 +522,39 @@ public class Administration{
}
}
/** Defines a (potentially dangerous) action that a player has done in the world.
* These objects are pooled; do not cache them! */
public static class PlayerAction implements Poolable{
public @NonNull Player player;
public @NonNull ActionType type;
public @NonNull Tile tile;
/** valid for configure and rotation-type events only. */
public int config;
/** valid for item-type events only. */
public @Nullable Item item;
public int itemAmount;
public PlayerAction set(Player player, ActionType type, Tile tile){
this.player = player;
this.type = type;
this.tile = tile;
return this;
}
@Override
public void reset(){
item = null;
itemAmount = config = 0;
player = null;
type = null;
tile = null;
}
}
public enum ActionType{
breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem
}
}

View File

@@ -15,8 +15,7 @@ import static mindustry.Vars.netServer;
public abstract class NetConnection{
public final String address;
public boolean mobile, modclient;
public @Nullable
Player player;
public @Nullable Player player;
/** ID of last recieved client snapshot. */
public int lastRecievedClientSnapshot = -1;
@@ -37,7 +36,7 @@ public abstract class NetConnection{
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
PlayerInfo info = netServer.admins.getInfo(player.uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
info.lastKicked = Math.max(Time.millis() + 30 * 1000, info.lastKicked);
}
Call.onKick(this, reason);
@@ -49,12 +48,17 @@ public abstract class NetConnection{
/** Kick with an arbitrary reason. */
public void kick(String reason){
kick(reason, 30 * 1000);
}
/** Kick with an arbitrary reason, and a kick duration in milliseconds. */
public void kick(String reason, int kickDuration){
Log.info("Kicking connection {0}; Reason: {1}", address, reason.replace("\n", " "));
if(player != null && player.uuid != null){
PlayerInfo info = netServer.admins.getInfo(player.uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
info.lastKicked = Math.max(Time.millis() + kickDuration, info.lastKicked);
}
Call.onKick(this, reason);

View File

@@ -2,9 +2,11 @@ package mindustry.type;
import arc.Core;
import arc.graphics.Color;
import arc.graphics.g2d.TextureRegion;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.scene.ui.layout.Table;
import arc.util.ArcAnnotate.*;
import arc.util.Time;
import mindustry.ctype.ContentType;
import mindustry.entities.type.Player;
import mindustry.ctype.UnlockableContent;
@@ -32,6 +34,13 @@ public class Mech extends UnlockableContent{
public boolean canHeal = false;
public float compoundSpeed, compoundSpeedBoost;
/** draw the health and team indicator */
public boolean drawCell = true;
/** draw the items on its back */
public boolean drawItems = true;
/** draw the engine light if it's flying/boosting */
public boolean drawLight = true;
public float weaponOffsetX, weaponOffsetY, engineOffset = 5f, engineSize = 2.5f;
public @NonNull Weapon weapon;
@@ -52,6 +61,24 @@ public class Mech extends UnlockableContent{
public void draw(Player player){
}
public void drawStats(Player player){
if(drawCell){
float health = player.healthf();
Draw.color(Color.black, player.getTeam().color, health + Mathf.absin(Time.time(), health * 5f, 1f - health));
Draw.rect(player.getPowerCellRegion(),
player.x + Angles.trnsx(player.rotation, cellTrnsY, 0f),
player.y + Angles.trnsy(player.rotation, cellTrnsY, 0f),
player.rotation - 90);
Draw.reset();
}
if(drawItems){
player.drawBackItems();
}
if(drawLight){
player.drawLight();
}
}
public float getExtraArmor(Player player){
return 0f;
}

View File

@@ -95,7 +95,7 @@ public class AboutDialog extends FloatingDialog{
public void showCredits(){
FloatingDialog dialog = new FloatingDialog("$credits");
dialog.addCloseButton();
dialog.cont.add("$credits.text");
dialog.cont.add("$credits.text").fillX().wrap().get().setAlignment(Align.center);
dialog.cont.row();
if(!contributors.isEmpty()){
dialog.cont.addImage().color(Pal.accent).fillX().height(3f).pad(3f);

View File

@@ -268,6 +268,7 @@ public class SettingsMenuDialog extends SettingsDialog{
}
return s + "%";
});
graphics.sliderPref("bridgeopacity", 75, 0, 100, 5, s -> s + "%");
if(!mobile){
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));

View File

@@ -55,6 +55,8 @@ public class Block extends BlockStorage{
public boolean rotate;
/** whether you can break this with rightclick */
public boolean breakable;
/** whether to add this block to brokenblocks */
public boolean rebuildable = true;
/** whether this floor can be placed on. */
public boolean placeableOn = true;
/** whether this block has insulating properties. */

View File

@@ -26,6 +26,7 @@ public class ShockMine extends Block{
solid = false;
targetable = false;
layer = Layer.overlay;
rebuildable = false;
}
@Override

View File

@@ -1,9 +1,10 @@
package mindustry.world.blocks.distribution;
import arc.*;
import arc.graphics.g2d.*;
import arc.math.Mathf;
import arc.math.geom.Geometry;
import mindustry.world.Tile;
import arc.math.*;
import arc.math.geom.*;
import mindustry.world.*;
import static mindustry.Vars.*;
@@ -31,6 +32,10 @@ public class ExtendingItemBridge extends ItemBridge{
ex *= uptime;
ey *= uptime;
float opacity = Core.settings.getInt("bridgeopacity") / 100f;
if(Mathf.zero(opacity)) return;
Draw.alpha(opacity);
Lines.stroke(8f);
Lines.line(bridgeRegion,
tile.worldx() + Geometry.d4[i].x * tilesize / 2f,
@@ -50,7 +55,7 @@ public class ExtendingItemBridge extends ItemBridge{
Draw.color();
for(int a = 0; a < arrows; a++){
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * uptime);
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * uptime * opacity);
Draw.rect(arrowRegion,
tile.worldx() + Geometry.d4[i].x * (tilesize / 2f + a * 6f + 2) * uptime,
tile.worldy() + Geometry.d4[i].y * (tilesize / 2f + a * 6f + 2) * uptime, i * 90f);

View File

@@ -219,10 +219,13 @@ public class ItemBridge extends Block{
Tile other = world.tile(entity.link);
if(!linkValid(tile, other)) return;
float opacity = Core.settings.getInt("bridgeopacity") / 100f;
if(Mathf.zero(opacity)) return;
int i = tile.absoluteRelativeTo(other.x, other.y);
Draw.color(Color.white, Color.black, Mathf.absin(Time.time(), 6f, 0.07f));
Draw.alpha(Math.max(entity.uptime, 0.25f));
Draw.alpha(Math.max(entity.uptime, 0.25f) * opacity);
Draw.rect(endRegion, tile.drawx(), tile.drawy(), i * 90 + 90);
Draw.rect(endRegion, other.drawx(), other.drawy(), i * 90 + 270);
@@ -242,7 +245,7 @@ public class ItemBridge extends Block{
Draw.color();
for(int a = 0; a < arrows; a++){
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * entity.uptime);
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * entity.uptime * opacity);
Draw.rect(arrowRegion,
tile.worldx() + Geometry.d4[i].x * (tilesize / 2f + a * 4f + time % 4f),
tile.worldy() + Geometry.d4[i].y * (tilesize / 2f + a * 4f + time % 4f), i * 90f);

View File

@@ -163,14 +163,22 @@ public class MassDriver extends Block{
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize, y*tilesize, range, Pal.accent);
// check if a mass driver is selected while placing this driver
//check if a mass driver is selected while placing this driver
if(!control.input.frag.config.isShown()) return;
Tile selected = control.input.frag.config.getSelectedTile();
if(!(selected.block() instanceof MassDriver) || !(selected.dst(x * tilesize, y * tilesize) <= range)) return;
if(selected == null || !(selected.block() instanceof MassDriver) || !(selected.dst(x * tilesize, y * tilesize) <= range)) return;
// if so, draw a dotted line towards it while it is in range
//if so, draw a dotted line towards it while it is in range
float sin = Mathf.absin(Time.time(), 6f, 1f);
Tmp.v1.set(x * tilesize + offset(), y * tilesize + offset()).sub(selected.drawx(), selected.drawy()).limit((size / 2f + 1) * tilesize + sin + 0.5f);
float x2 = x * tilesize - Tmp.v1.x, y2 = y * tilesize - Tmp.v1.y,
x1 = selected.drawx() + Tmp.v1.x, y1 = selected.drawy() + Tmp.v1.y;
int segs = (int)(selected.dst(x * tilesize, y * tilesize) / tilesize);
Lines.stroke(4f, Pal.gray);
Lines.dashLine(x1, y1, x2, y2, segs);
Lines.stroke(2f, Pal.placing);
Lines.dashLine(x * tilesize, y * tilesize, selected.drawx(), selected.drawy(), (int)range / tilesize / 4);
Lines.dashLine(x1, y1, x2, y2, segs);
Draw.reset();
}

View File

@@ -46,6 +46,7 @@ public class NuclearReactor extends PowerGenerator{
hasItems = true;
hasLiquids = true;
entityType = NuclearReactorEntity::new;
rebuildable = false;
}
@Override

View File

@@ -27,7 +27,7 @@ public class PowerDiode extends Block{
public void update(Tile tile){
super.update(tile);
if(tile.front() == null || tile.back() == null || !tile.back().block().hasPower || !tile.front().block().hasPower) return;
if(tile.front() == null || tile.back() == null || !tile.back().block().hasPower || !tile.front().block().hasPower || tile.back().getTeam() != tile.front().getTeam()) return;
PowerGraph backGraph = tile.back().entity.power.graph;
PowerGraph frontGraph = tile.front().entity.power.graph;

View File

@@ -0,0 +1,29 @@
package mindustry.world.blocks.sandbox;
import mindustry.type.*;
import mindustry.world.*;
public class LiquidVoid extends Block{
public LiquidVoid(String name){
super(name);
hasLiquids = true;
solid = true;
update = true;
}
@Override
public void setBars(){
super.setBars();
bars.remove("liquid");
}
@Override
public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){
return true;
}
@Override
public void handleLiquid(Tile tile, Tile source, Liquid liquid, float amount){}
}

View File

@@ -73,7 +73,7 @@ public class LaunchPad extends StorageBlock{
public void update(Tile tile){
TileEntity entity = tile.entity;
if(world.isZone() && entity.cons.valid() && world.isZone() && entity.items.total() >= itemCapacity && entity.timer.get(timerLaunch, launchTime / entity.timeScale)){
if(world.isZone() && entity.cons.valid() && entity.items.total() >= itemCapacity && entity.timer.get(timerLaunch, launchTime / entity.timeScale)){
for(Item item : Vars.content.items()){
Events.fire(Trigger.itemLaunch);
Effects.effect(Fx.padlaunch, tile);