diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index d8b310dfb7..25e68576e4 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -170,7 +170,8 @@ public class NetServer implements ApplicationListener{ return; } - if(admins.isIDBanned(uuid)){ + //there's no reason to tell users that their name is inappropriate, as they may try to bypass it + if(admins.isIDBanned(uuid) || admins.isNameBanned(packet.name)){ con.kick(KickReason.banned); return; } diff --git a/core/src/mindustry/net/Administration.java b/core/src/mindustry/net/Administration.java index 76d21c6028..f1c107d50a 100644 --- a/core/src/mindustry/net/Administration.java +++ b/core/src/mindustry/net/Administration.java @@ -13,6 +13,8 @@ import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.payloads.*; +import java.util.regex.*; + import static mindustry.Vars.*; import static mindustry.game.EventType.*; @@ -24,6 +26,7 @@ public class Administration{ public Seq subnetBans = new Seq<>(); public ObjectSet dosBlacklist = new ObjectSet<>(); public ObjectMap kickedIPs = new ObjectMap<>(); + public Seq bannedNames = new Seq<>(); private boolean modified, loaded; /** All player info. Maps UUIDs to info. This persists throughout restarts. Do not modify directly. */ @@ -131,6 +134,11 @@ public class Administration{ return subnetBans.contains(ip::startsWith); } + public void addNameBan(String regex) throws PatternSyntaxException{ + bannedNames.add(Pattern.compile(regex, Pattern.CASE_INSENSITIVE)); + save(); + } + /** Adds a chat filter. This will transform the chat messages of every player. * This functionality can be used to implement things like swear filters and special commands. * Note that commands (starting with /) are not filtered.*/ @@ -376,6 +384,10 @@ public class Administration{ return getCreateInfo(uuid).banned; } + public boolean isNameBanned(String name){ + return bannedNames.size > 0 && bannedNames.contains(p -> p.matcher(name).find()); + } + public boolean isAdmin(String id, String usid){ PlayerInfo info = getCreateInfo(id); return info.admin && usid.equals(info.adminUsid); @@ -464,6 +476,7 @@ public class Administration{ Core.settings.putJson("ip-bans", String.class, bannedIPs); Core.settings.putJson("whitelist-ids", String.class, whitelist); Core.settings.putJson("banned-subnets", String.class, subnetBans); + Core.settings.putJson("banned-names", String.class, bannedNames.map(Pattern::pattern)); modified = false; } } @@ -477,6 +490,14 @@ public class Administration{ bannedIPs = Core.settings.getJson("ip-bans", Seq.class, Seq::new); whitelist = Core.settings.getJson("whitelist-ids", Seq.class, Seq::new); subnetBans = Core.settings.getJson("banned-subnets", Seq.class, Seq::new); + + Seq nameRegexes = Core.settings.getJson("banned-names", Seq.class, String.class, Seq::new); + for(var regex : nameRegexes){ + try{ + bannedNames.add(Pattern.compile(regex, Pattern.CASE_INSENSITIVE)); + }catch(Exception ignored){ + } + } } /** diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 010a7c0268..77c4f91d3d 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -1500,6 +1500,9 @@ public class UnitType extends UnlockableContent implements Senseable{ Draw.z(z); if(unit instanceof Crawlc c){ + if(isPayload){ + c.segmentRot(c.rotation()); + } drawCrawl(c); } diff --git a/core/src/mindustry/world/blocks/production/BeamDrill.java b/core/src/mindustry/world/blocks/production/BeamDrill.java index a142f890ea..79acd896c1 100644 --- a/core/src/mindustry/world/blocks/production/BeamDrill.java +++ b/core/src/mindustry/world/blocks/production/BeamDrill.java @@ -69,7 +69,7 @@ public class BeamDrill extends Block{ solid = true; drawArrow = false; regionRotated1 = 1; - ignoreLineRotation = true; + if(!mobile) ignoreLineRotation = true; ambientSoundVolume = 0.05f; ambientSound = Sounds.loopMineBeam; diff --git a/core/src/mindustry/world/blocks/production/WallCrafter.java b/core/src/mindustry/world/blocks/production/WallCrafter.java index bc3dfb0ae7..76da4a79fa 100644 --- a/core/src/mindustry/world/blocks/production/WallCrafter.java +++ b/core/src/mindustry/world/blocks/production/WallCrafter.java @@ -57,7 +57,7 @@ public class WallCrafter extends Block{ rotate = true; update = true; solid = true; - ignoreLineRotation = true; + if(!mobile) ignoreLineRotation = true; regionRotated1 = 1; envEnabled |= Env.space; diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 89ca7b0d02..6ca07064db 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -32,6 +32,7 @@ import java.net.*; import java.time.*; import java.time.format.*; import java.util.*; +import java.util.regex.*; import static arc.util.ColorCodes.*; import static arc.util.Log.*; @@ -731,6 +732,50 @@ public class ServerControl implements ApplicationListener{ } }); + handler.register("name-ban", "[add/remove/clear] [regex]", "Ban a name by case-insensitive regex.", arg -> { + var names = netServer.admins.bannedNames; + + if(arg.length == 0){ + info("Name regexes banned: @", names.isEmpty() ? "" : ""); + for(Pattern subnet : names){ + info("&lw\t" + subnet.pattern()); + } + }else if(arg.length == 1){ + if(arg[0].equals("clear")){ + names.clear(); + netServer.admins.save(); + }else{ + err("You must provide a name regex to add or remove."); + } + }else{ + if(arg[0].equals("add")){ + if(names.contains(p -> p.pattern().equals(arg[1]))){ + err("That name regex is already banned."); + return; + } + + try{ + netServer.admins.addNameBan(arg[1]); + info("Banned names by regex: @", arg[1]); + }catch(Exception e){ + err("Invalid regex: @", Strings.getSimpleMessage(e)); + } + }else if(arg[0].equals("remove")){ + int target = names.indexOf(p -> p.pattern().equals(arg[1])); + if(target == -1){ + err("That name isn't banned."); + return; + } + + names.remove(target); + netServer.admins.save(); + info("Unbanned regex: @", arg[1]); + }else{ + err("Incorrect usage. Provide add/remove as the second argument."); + } + } + }); + handler.register("whitelist", "[add/remove] [ID]", "Add/remove players from the whitelist using their ID.", arg -> { if(arg.length == 0){ Seq whitelist = netServer.admins.getWhitelisted();