Merge pull request 'Mod detection refactoring' (#509) from modDetectionRework into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #509 Reviewed-by: YoyoNow <jwsteam@nidido.de>
Dieser Commit ist enthalten in:
Commit
c74178abe1
21
build.gradle
21
build.gradle
@ -59,8 +59,26 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
maven {
|
||||||
url 'https://m2.dv8tion.net/releases'
|
url 'https://m2.dv8tion.net/releases'
|
||||||
|
content {
|
||||||
|
includeGroup 'net.dv8tion'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
url 'https://repo.lunarclient.dev'
|
||||||
|
content {
|
||||||
|
includeGroup 'com.lunarclient'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
exclude 'META-INF/*'
|
||||||
|
//https://imperceptiblethoughts.com/shadow/configuration/minimizing/
|
||||||
|
minimize {
|
||||||
|
exclude project(':')
|
||||||
|
}
|
||||||
|
duplicatesStrategy DuplicatesStrategy.INCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.22'
|
compileOnly 'org.projectlombok:lombok:1.18.22'
|
||||||
@ -75,4 +93,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
implementation project(":CommonCore")
|
implementation project(":CommonCore")
|
||||||
|
|
||||||
|
implementation 'com.lunarclient:apollo-api:1.1.0'
|
||||||
|
implementation 'com.lunarclient:apollo-common:1.1.0'
|
||||||
}
|
}
|
@ -23,11 +23,9 @@ import de.steamwar.bungeecore.bot.SteamwarDiscordBot;
|
|||||||
import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig;
|
import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig;
|
||||||
import de.steamwar.bungeecore.commands.*;
|
import de.steamwar.bungeecore.commands.*;
|
||||||
import de.steamwar.bungeecore.listeners.*;
|
import de.steamwar.bungeecore.listeners.*;
|
||||||
import de.steamwar.bungeecore.listeners.mods.*;
|
import de.steamwar.bungeecore.mods.ServerListPing;
|
||||||
import de.steamwar.bungeecore.listeners.ping.PingListener;
|
import de.steamwar.bungeecore.mods.*;
|
||||||
import de.steamwar.bungeecore.network.BungeeNetworkHandler;
|
import de.steamwar.bungeecore.network.BungeeNetworkHandler;
|
||||||
import de.steamwar.bungeecore.network.NetworkReceiver;
|
|
||||||
import de.steamwar.bungeecore.network.SWScriptSyntaxForwarder;
|
|
||||||
import de.steamwar.bungeecore.tablist.TablistManager;
|
import de.steamwar.bungeecore.tablist.TablistManager;
|
||||||
import de.steamwar.command.SWCommandUtils;
|
import de.steamwar.command.SWCommandUtils;
|
||||||
import de.steamwar.command.SWTypeMapperCreator;
|
import de.steamwar.command.SWTypeMapperCreator;
|
||||||
@ -38,7 +36,6 @@ import de.steamwar.sql.SteamwarUser;
|
|||||||
import de.steamwar.sql.Team;
|
import de.steamwar.sql.Team;
|
||||||
import de.steamwar.sql.UserElo;
|
import de.steamwar.sql.UserElo;
|
||||||
import de.steamwar.sql.internal.Statement;
|
import de.steamwar.sql.internal.Statement;
|
||||||
import net.md_5.bungee.BungeeCord;
|
|
||||||
import net.md_5.bungee.api.ChatMessageType;
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
@ -76,11 +73,6 @@ public class BungeeCore extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable(){
|
public void onEnable(){
|
||||||
getProxy().registerChannel("sw:bridge");
|
|
||||||
getProxy().registerChannel("fabricmodsender:mods");
|
|
||||||
getProxy().registerChannel("nochatreports:sync");
|
|
||||||
getProxy().registerChannel("sw:script_syntax");
|
|
||||||
|
|
||||||
setInstance(this);
|
setInstance(this);
|
||||||
MAIN_SERVER = ProxyServer.getInstance().getConfig().getListeners().stream().anyMatch(info -> ((InetSocketAddress) info.getSocketAddress()).getPort() == 25565);
|
MAIN_SERVER = ProxyServer.getInstance().getConfig().getListeners().stream().anyMatch(info -> ((InetSocketAddress) info.getSocketAddress()).getPort() == 25565);
|
||||||
loadConfig();
|
loadConfig();
|
||||||
@ -98,25 +90,22 @@ public class BungeeCore extends Plugin {
|
|||||||
return tabCompleter.apply(sender, s);
|
return tabCompleter.apply(sender, s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
BungeeCord.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS);
|
ProxyServer.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS);
|
||||||
|
|
||||||
new NonFabricFabricCheck();
|
new Hostname();
|
||||||
|
new ServerListPing();
|
||||||
new SWScriptSyntaxForwarder();
|
new PluginMessage();
|
||||||
new ConnectionListener();
|
new Schematica();
|
||||||
new Forge();
|
|
||||||
new Forge12();
|
|
||||||
new LabyMod();
|
|
||||||
new Badlion();
|
new Badlion();
|
||||||
|
new FabricModSender();
|
||||||
|
new ReplayMod();
|
||||||
|
new FML2();
|
||||||
|
|
||||||
|
new ConnectionListener();
|
||||||
new ChatListener();
|
new ChatListener();
|
||||||
new BanListener();
|
new BanListener();
|
||||||
new CheckListener();
|
new CheckListener();
|
||||||
new ModLoaderBlocker();
|
new IPSanitizer();
|
||||||
new WorldDownloader();
|
|
||||||
new BrandListener();
|
|
||||||
new Fabric();
|
|
||||||
new SubserverProtocolFixer();
|
|
||||||
new PingListener();
|
|
||||||
|
|
||||||
local = new Node.LocalNode();
|
local = new Node.LocalNode();
|
||||||
if(MAIN_SERVER) {
|
if(MAIN_SERVER) {
|
||||||
@ -186,7 +175,6 @@ public class BungeeCore extends Plugin {
|
|||||||
|
|
||||||
new EventStarter();
|
new EventStarter();
|
||||||
new SessionManager();
|
new SessionManager();
|
||||||
new NetworkReceiver();
|
|
||||||
BungeeNetworkHandler.register();
|
BungeeNetworkHandler.register();
|
||||||
tablistManager = new TablistManager();
|
tablistManager = new TablistManager();
|
||||||
new SettingsChangedListener();
|
new SettingsChangedListener();
|
||||||
@ -321,9 +309,6 @@ public class BungeeCore extends Plugin {
|
|||||||
serverName,
|
serverName,
|
||||||
cmds.toArray(new String[0])
|
cmds.toArray(new String[0])
|
||||||
);
|
);
|
||||||
if(server.getBoolean("modchecked", false)) {
|
|
||||||
ModLoaderBlocker.addServer(serverName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File discordFile = new File(System.getProperty("user.home"), "discord.yml");
|
File discordFile = new File(System.getProperty("user.home"), "discord.yml");
|
||||||
|
@ -31,6 +31,7 @@ import java.util.logging.Level;
|
|||||||
|
|
||||||
public abstract class Node {
|
public abstract class Node {
|
||||||
|
|
||||||
|
//-Xquickstart Langzeitperformance testen!
|
||||||
private static final List<String> OPENJ9_ARGS = Arrays.asList(
|
private static final List<String> OPENJ9_ARGS = Arrays.asList(
|
||||||
"-XX:+EnableCRIUSupport", "-XX:-CRIURestoreNonPortableMode",
|
"-XX:+EnableCRIUSupport", "-XX:-CRIURestoreNonPortableMode",
|
||||||
"-Xgc:excessiveGCratio=80", "-Xdisableexplicitgc", "-Xnoclassgc", "-Xmos128M", "-Xmns48M", "-XX:+ExitOnOutOfMemoryError", // initial heap half values of memory observed by 1.19 spectate server
|
"-Xgc:excessiveGCratio=80", "-Xdisableexplicitgc", "-Xnoclassgc", "-Xmos128M", "-Xmns48M", "-XX:+ExitOnOutOfMemoryError", // initial heap half values of memory observed by 1.19 spectate server
|
||||||
|
@ -23,6 +23,7 @@ import com.google.gson.JsonParser;
|
|||||||
import de.steamwar.bungeecore.BungeeCore;
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
import de.steamwar.bungeecore.Message;
|
import de.steamwar.bungeecore.Message;
|
||||||
import de.steamwar.bungeecore.listeners.ConnectionListener;
|
import de.steamwar.bungeecore.listeners.ConnectionListener;
|
||||||
|
import de.steamwar.bungeecore.listeners.IPSanitizer;
|
||||||
import de.steamwar.command.PreviousArguments;
|
import de.steamwar.command.PreviousArguments;
|
||||||
import de.steamwar.command.SWCommand;
|
import de.steamwar.command.SWCommand;
|
||||||
import de.steamwar.command.TypeMapper;
|
import de.steamwar.command.TypeMapper;
|
||||||
@ -96,14 +97,15 @@ public class PunishmentCommand {
|
|||||||
|
|
||||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(user.getUUID());
|
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(user.getUUID());
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
|
String ip = IPSanitizer.getTrueAddress(player.getPendingConnection()).getHostAddress();
|
||||||
ChatSender.disconnect(player).system(punishmentMessage(user, Punishment.PunishmentType.Ban));
|
ChatSender.disconnect(player).system(punishmentMessage(user, Punishment.PunishmentType.Ban));
|
||||||
for (BannedUserIPs banned : BannedUserIPs.get(player.getAddress().getAddress().getHostAddress())) {
|
for (BannedUserIPs banned : BannedUserIPs.get(ip)) {
|
||||||
SteamwarUser bannedUser = SteamwarUser.get(banned.getUserID());
|
SteamwarUser bannedUser = SteamwarUser.get(banned.getUserID());
|
||||||
if (bannedUser.isPunished(Punishment.PunishmentType.Ban) && bannedUser.getPunishment(Punishment.PunishmentType.Ban).getEndTime().before(time)) {
|
if (bannedUser.isPunished(Punishment.PunishmentType.Ban) && bannedUser.getPunishment(Punishment.PunishmentType.Ban).getEndTime().before(time)) {
|
||||||
bannedUser.punish(Punishment.PunishmentType.Ban, time, banReason, punisher.getId(), perma);
|
bannedUser.punish(Punishment.PunishmentType.Ban, time, banReason, punisher.getId(), perma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BannedUserIPs.banIP(user.getId(), player.getAddress().getAddress().getHostAddress());
|
BannedUserIPs.banIP(user.getId(), ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
package de.steamwar.bungeecore.commands;
|
package de.steamwar.bungeecore.commands;
|
||||||
|
|
||||||
import de.steamwar.bungeecore.ArenaMode;
|
import de.steamwar.bungeecore.ArenaMode;
|
||||||
import de.steamwar.bungeecore.listeners.mods.ModLoaderBlocker;
|
|
||||||
import de.steamwar.command.SWCommandUtils;
|
import de.steamwar.command.SWCommandUtils;
|
||||||
import de.steamwar.command.TypeMapper;
|
import de.steamwar.command.TypeMapper;
|
||||||
import de.steamwar.command.TypeValidator;
|
import de.steamwar.command.TypeValidator;
|
||||||
@ -49,10 +48,6 @@ public class TypeMappers {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModLoaderBlocker.isFabric(value)) {
|
|
||||||
messageSender.send("MODLOADER_DENIED");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ package de.steamwar.bungeecore.commands;
|
|||||||
|
|
||||||
import de.steamwar.bungeecore.Message;
|
import de.steamwar.bungeecore.Message;
|
||||||
import de.steamwar.bungeecore.Storage;
|
import de.steamwar.bungeecore.Storage;
|
||||||
import de.steamwar.bungeecore.listeners.mods.Utils;
|
import de.steamwar.bungeecore.mods.ModUtils;
|
||||||
import de.steamwar.command.SWCommand;
|
import de.steamwar.command.SWCommand;
|
||||||
import de.steamwar.command.SWCommandUtils;
|
import de.steamwar.command.SWCommandUtils;
|
||||||
import de.steamwar.command.TypeMapper;
|
import de.steamwar.command.TypeMapper;
|
||||||
@ -118,7 +118,7 @@ public class WhoisCommand extends SWCommand {
|
|||||||
sender.system("WHOIS_CURRENT_SERVER", target.getServer().getInfo().getName());
|
sender.system("WHOIS_CURRENT_SERVER", target.getServer().getInfo().getName());
|
||||||
sender.system("WHOIS_CURRENT_PROTOCOL", target.getPendingConnection().getVersion());
|
sender.system("WHOIS_CURRENT_PROTOCOL", target.getPendingConnection().getVersion());
|
||||||
|
|
||||||
List<Mod> mods = Utils.playerModMap.get(user.getUUID());
|
List<Mod> mods = ModUtils.getPlayerModMap().get(user.getUUID());
|
||||||
if(mods == null)
|
if(mods == null)
|
||||||
mods = Collections.emptyList();
|
mods = Collections.emptyList();
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import de.steamwar.bungeecore.BungeeCore;
|
|||||||
import de.steamwar.bungeecore.Message;
|
import de.steamwar.bungeecore.Message;
|
||||||
import de.steamwar.bungeecore.commands.PunishmentCommand;
|
import de.steamwar.bungeecore.commands.PunishmentCommand;
|
||||||
import de.steamwar.bungeecore.commands.WebpasswordCommand;
|
import de.steamwar.bungeecore.commands.WebpasswordCommand;
|
||||||
import de.steamwar.bungeecore.listeners.mods.Forge;
|
|
||||||
import de.steamwar.messages.ChatSender;
|
import de.steamwar.messages.ChatSender;
|
||||||
import de.steamwar.sql.BannedUserIPs;
|
import de.steamwar.sql.BannedUserIPs;
|
||||||
import de.steamwar.sql.Punishment;
|
import de.steamwar.sql.Punishment;
|
||||||
@ -47,17 +46,17 @@ public class BanListener extends BasicListener {
|
|||||||
ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> {
|
ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> {
|
||||||
PendingConnection connection = event.getConnection();
|
PendingConnection connection = event.getConnection();
|
||||||
SteamwarUser user = SteamwarUser.getOrCreate(connection.getUniqueId(), connection.getName(), ConnectionListener::newPlayer, WebpasswordCommand::changeUsername);
|
SteamwarUser user = SteamwarUser.getOrCreate(connection.getUniqueId(), connection.getName(), ConnectionListener::newPlayer, WebpasswordCommand::changeUsername);
|
||||||
|
String ip = IPSanitizer.getTrueAddress(connection).getHostAddress();
|
||||||
if (user.isPunished(Punishment.PunishmentType.Ban)) {
|
if (user.isPunished(Punishment.PunishmentType.Ban)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
BannedUserIPs.banIP(user.getId(), connection.getAddress().getAddress().getHostAddress());
|
BannedUserIPs.banIP(user.getId(), ip);
|
||||||
ChatSender.of(event).system(PunishmentCommand.punishmentMessage(user, Punishment.PunishmentType.Ban));
|
ChatSender.of(event).system(PunishmentCommand.punishmentMessage(user, Punishment.PunishmentType.Ban));
|
||||||
event.completeIntent(BungeeCore.get());
|
event.completeIntent(BungeeCore.get());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BannedUserIPs> ips = BannedUserIPs.get(connection.getAddress().getAddress().getHostAddress());
|
List<BannedUserIPs> ips = BannedUserIPs.get(ip);
|
||||||
if(!ips.isEmpty()){
|
if(!ips.isEmpty()){
|
||||||
|
|
||||||
Timestamp highestBan = ips.get(0).getTimestamp();
|
Timestamp highestBan = ips.get(0).getTimestamp();
|
||||||
boolean perma = false;
|
boolean perma = false;
|
||||||
for(BannedUserIPs banned : ips) {
|
for(BannedUserIPs banned : ips) {
|
||||||
@ -74,7 +73,7 @@ public class BanListener extends BasicListener {
|
|||||||
}
|
}
|
||||||
ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/ban " + user.getUserName() + " "
|
ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/ban " + user.getUserName() + " "
|
||||||
+ (perma?"perma":highestBan.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy_HH:mm")))
|
+ (perma?"perma":highestBan.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy_HH:mm")))
|
||||||
+ " Bannumgehung");
|
+ " Ban Evasion - Bannumgehung");
|
||||||
|
|
||||||
ChatSender.serverteamReceivers().forEach(sender -> sender.system(
|
ChatSender.serverteamReceivers().forEach(sender -> sender.system(
|
||||||
"BAN_AVOIDING_ALERT",
|
"BAN_AVOIDING_ALERT",
|
||||||
@ -89,7 +88,7 @@ public class BanListener extends BasicListener {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Forge.onServerConnected(event);
|
event.completeIntent(BungeeCore.get());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners;
|
|
||||||
|
|
||||||
import de.steamwar.messages.ChatSender;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
import net.md_5.bungee.protocol.DefinedPacket;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.net.SocketException;
|
|
||||||
|
|
||||||
public class BrandListener extends BasicListener {
|
|
||||||
|
|
||||||
private static boolean isLocalHost(InetAddress addr) {
|
|
||||||
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return NetworkInterface.getByInetAddress(addr) != null;
|
|
||||||
} catch (SocketException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onServerSwitch(PluginMessageEvent event) {
|
|
||||||
if(!event.getTag().equals("minecraft:brand") && !event.getTag().equals("MC|Brand")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.getReceiver() == null || isLocalHost(event.getReceiver().getAddress().getAddress())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(event.getReceiver() instanceof ProxiedPlayer)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.setCancelled(true);
|
|
||||||
|
|
||||||
ProxiedPlayer player = (ProxiedPlayer) event.getReceiver();
|
|
||||||
String brandString = ChatSender.of(player).parseToLegacy("STEAMWAR_BRAND", ProxyServer.getInstance().getName(), player.getServer().getInfo().getName(), new String(event.getData(), 1, event.getData().length - 1));
|
|
||||||
|
|
||||||
ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer();
|
|
||||||
DefinedPacket.writeString(brandString, brand);
|
|
||||||
player.sendData(event.getTag(), DefinedPacket.toArray(brand));
|
|
||||||
brand.release();
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,7 +29,7 @@ import de.steamwar.bungeecore.commands.ChallengeCommand;
|
|||||||
import de.steamwar.bungeecore.commands.CheckCommand;
|
import de.steamwar.bungeecore.commands.CheckCommand;
|
||||||
import de.steamwar.bungeecore.commands.ModCommand;
|
import de.steamwar.bungeecore.commands.ModCommand;
|
||||||
import de.steamwar.bungeecore.commands.MsgCommand;
|
import de.steamwar.bungeecore.commands.MsgCommand;
|
||||||
import de.steamwar.bungeecore.listeners.mods.Utils;
|
import de.steamwar.bungeecore.mods.ModUtils;
|
||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import de.steamwar.sql.UserPerm;
|
import de.steamwar.sql.UserPerm;
|
||||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||||
@ -156,7 +156,7 @@ public class ConnectionListener extends BasicListener {
|
|||||||
public void onDisconnect(PlayerDisconnectEvent e){
|
public void onDisconnect(PlayerDisconnectEvent e){
|
||||||
ChallengeCommand.remove(e.getPlayer());
|
ChallengeCommand.remove(e.getPlayer());
|
||||||
MsgCommand.remove(e.getPlayer());
|
MsgCommand.remove(e.getPlayer());
|
||||||
Utils.playerModMap.remove(e.getPlayer().getUniqueId());
|
ModUtils.getPlayerModMap().remove(e.getPlayer().getUniqueId());
|
||||||
ModCommand.playerFilterType.remove(e.getPlayer());
|
ModCommand.playerFilterType.remove(e.getPlayer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
66
src/de/steamwar/bungeecore/listeners/IPSanitizer.java
Normale Datei
66
src/de/steamwar/bungeecore/listeners/IPSanitizer.java
Normale Datei
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.listeners;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class IPSanitizer extends BasicListener {
|
||||||
|
|
||||||
|
private static final Field initialHandlerCh;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
initialHandlerCh = InitialHandler.class.getDeclaredField("ch");
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new SecurityException("Could not initialize Reflection", e);
|
||||||
|
}
|
||||||
|
initialHandlerCh.setAccessible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChannelWrapper getChannelWrapper(PendingConnection connection) {
|
||||||
|
try {
|
||||||
|
return (ChannelWrapper) initialHandlerCh.get(connection);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new SecurityException("Could not get channel wrapper", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InetAddress getTrueAddress(PendingConnection connection) {
|
||||||
|
return ((InetSocketAddress) getChannelWrapper(connection).getHandle().remoteAddress()).getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final InetSocketAddress sanitized = new InetSocketAddress("127.127.127.127", 25565);
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void loginEvent(LoginEvent e) {
|
||||||
|
BungeeCore.get().getLogger().log(Level.INFO, e.getConnection().getSocketAddress() + " has logged in with user name " + e.getConnection().getName());
|
||||||
|
getChannelWrapper(e.getConnection()).setRemoteAddress(sanitized);
|
||||||
|
}
|
||||||
|
}
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.Message;
|
|
||||||
import de.steamwar.bungeecore.Storage;
|
|
||||||
import net.md_5.bungee.BungeeCord;
|
|
||||||
import net.md_5.bungee.api.connection.Connection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class NonFabricFabricCheck extends BasicListener {
|
|
||||||
|
|
||||||
private final Set<UUID> usingFabric = new HashSet<>();
|
|
||||||
|
|
||||||
private final Set<ProxiedPlayer> checking = new HashSet<>();
|
|
||||||
private final Set<ProxiedPlayer> vanilla = new HashSet<>();
|
|
||||||
|
|
||||||
{
|
|
||||||
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), usingFabric::clear, 0, 15, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void pluginMessageEvent(PluginMessageEvent e) {
|
|
||||||
Connection sender = e.getSender();
|
|
||||||
if(!(sender instanceof ProxiedPlayer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
ProxiedPlayer p = (ProxiedPlayer) sender;
|
|
||||||
if (e.getTag().equals("minecraft:register") && new String(e.getData()).contains("fabric-screen-handler-api-v1:open_screen")) {
|
|
||||||
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
|
||||||
if (!sender.isConnected()) return;
|
|
||||||
if (!vanilla.remove(p)) return;
|
|
||||||
if (Storage.fabricCheckedPlayers.containsKey(p)) return;
|
|
||||||
p.disconnect(Message.parse("MOD_USE_MODSENDER", p));
|
|
||||||
}, 25, TimeUnit.SECONDS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!e.getTag().equals("minecraft:brand"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!new String(e.getData()).equals("vanilla"))
|
|
||||||
return;
|
|
||||||
vanilla.add(p);
|
|
||||||
|
|
||||||
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
|
||||||
if (!p.isConnected()) return;
|
|
||||||
if (Storage.fabricCheckedPlayers.containsKey(p)) return;
|
|
||||||
checking.add(p);
|
|
||||||
p.sendData("fabric-screen-handler-api-v1:open_screen", new byte[] {0});
|
|
||||||
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> checking.remove(p), 1, TimeUnit.SECONDS);
|
|
||||||
}, 30, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerPreLogin(LoginEvent e) {
|
|
||||||
if (usingFabric.remove(e.getConnection().getUniqueId())) {
|
|
||||||
e.getConnection().disconnect(Message.parse("MOD_USE_MODSENDER", Locale.getDefault()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerDisconnect(PlayerDisconnectEvent e) {
|
|
||||||
if (checking.remove(e.getPlayer())) {
|
|
||||||
usingFabric.add(e.getPlayer().getUniqueId());
|
|
||||||
}
|
|
||||||
vanilla.remove(e.getPlayer());
|
|
||||||
}
|
|
||||||
}
|
|
216
src/de/steamwar/bungeecore/listeners/PluginMessage.java
Normale Datei
216
src/de/steamwar/bungeecore/listeners/PluginMessage.java
Normale Datei
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.listeners;
|
||||||
|
|
||||||
|
import com.lunarclient.apollo.ApolloManager;
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.commands.TeamCommand;
|
||||||
|
import de.steamwar.bungeecore.mods.*;
|
||||||
|
import de.steamwar.bungeecore.network.ServerMetaInfo;
|
||||||
|
import de.steamwar.messages.ChatSender;
|
||||||
|
import de.steamwar.network.packets.NetworkPacket;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.connection.Connection;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.connection.Server;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.protocol.DefinedPacket;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class PluginMessage extends BasicListener {
|
||||||
|
|
||||||
|
public static void send(ProxiedPlayer player, String legacyChannel, String channel, byte[] data) {
|
||||||
|
// 1.12 format change
|
||||||
|
send(player, player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(ProxiedPlayer player, String channel, byte[] data) {
|
||||||
|
if(((InitialHandler)player.getPendingConnection()).getRegisteredChannels().contains(channel))
|
||||||
|
player.sendData(channel, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Parser UNKNOWN = event -> BungeeCore.get().getLogger().log(Level.WARNING, () -> "Undefined PluginMessage on channel " + event.getTag() + " from " + event.getSender() + " received.\n" + Arrays.toString(event.getData()));
|
||||||
|
private static final Parser PASS_THROUGH = event -> event.setCancelled(false);
|
||||||
|
private static final Parser DROP = event -> {};
|
||||||
|
|
||||||
|
private final Lunar lunar = new Lunar();
|
||||||
|
|
||||||
|
private final Set<String> knownBrands = new HashSet<>();
|
||||||
|
private final Set<String> knownChannels = new HashSet<>();
|
||||||
|
private final Map<String, Parser> handlers = new HashMap<>();
|
||||||
|
|
||||||
|
public PluginMessage() {
|
||||||
|
LabyMod labyMod = new LabyMod();
|
||||||
|
FML fml = new FML();
|
||||||
|
FabricModSender fabricModSender = new FabricModSender();
|
||||||
|
WorldDownloader wdl = new WorldDownloader();
|
||||||
|
|
||||||
|
knownBrands.add("vanilla");
|
||||||
|
knownBrands.add("fabric");
|
||||||
|
knownBrands.add("forge"); //Forge registers all channels the server registers
|
||||||
|
|
||||||
|
knownChannels.add("fabric:container/open");
|
||||||
|
knownChannels.add("fabric:registry/sync/direct");
|
||||||
|
knownChannels.add("fabric-screen-handler-api-v1:open_screen");
|
||||||
|
|
||||||
|
knownChannels.add(FML.CHANNEL);
|
||||||
|
knownChannels.add("fml:loginwrapper");
|
||||||
|
knownChannels.add("fml:handshake");
|
||||||
|
knownChannels.add("fml:play");
|
||||||
|
knownChannels.add("forge:tier_sorting");
|
||||||
|
knownChannels.add("forge:split");
|
||||||
|
knownChannels.add("forge:login");
|
||||||
|
knownChannels.add("forge:handshake");
|
||||||
|
|
||||||
|
knownChannels.add(ApolloManager.PLUGIN_MESSAGE_CHANNEL);
|
||||||
|
|
||||||
|
knownChannels.add("Replay|Restrict");
|
||||||
|
knownChannels.add("replaymod:restrict");
|
||||||
|
knownChannels.add("WDL|CONTROL");
|
||||||
|
knownChannels.add("wdl:control");
|
||||||
|
knownChannels.add("worldedit:cui");
|
||||||
|
|
||||||
|
register("REGISTER", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
|
||||||
|
register("minecraft:register", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
|
||||||
|
|
||||||
|
register("BungeeCord", false, onlySWSource(PASS_THROUGH));
|
||||||
|
register("bungeecord:main", false, onlySWSource(PASS_THROUGH));
|
||||||
|
register("MC|Brand", false, directional(this::steamWarBrand, this::userBrand));
|
||||||
|
register("minecraft:brand", false, directional(this::steamWarBrand, this::userBrand));
|
||||||
|
|
||||||
|
register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN));
|
||||||
|
register("worldedit:cui", false, PASS_THROUGH);
|
||||||
|
register("fabricmodsender:mods", true, directional(UNKNOWN, fabricModSender::handlePluginMessage));
|
||||||
|
|
||||||
|
register("WDL|REQUEST", false, DROP);
|
||||||
|
register("wdl:request", false, DROP);
|
||||||
|
register("WDL|INIT", true, directional(UNKNOWN, wdl::handlePluginMessage));
|
||||||
|
register("wdl:init", true, directional(UNKNOWN, wdl::handlePluginMessage));
|
||||||
|
|
||||||
|
register(ApolloManager.PLUGIN_MESSAGE_CHANNEL, true, async(lunar::handlePluginMessage));
|
||||||
|
register("LMC", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
|
||||||
|
register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
|
||||||
|
register(FML.CHANNEL, true, directional(UNKNOWN, async(fml::handlePluginMessage)));
|
||||||
|
|
||||||
|
//vanilla does not register any channels (sends only one minecraft:brand vanilla, nothing else (potential spoofed client detection))
|
||||||
|
//meteor https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/ServerSpoof.java https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/DiscordPresence.java
|
||||||
|
//feather:client https://github.com/Koupah/Feather-Client-API/blob/main/src/club/koupah/feather/handler/FeatherHandler.java
|
||||||
|
//litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPluginMessage(PluginMessageEvent event) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
handlers.getOrDefault(event.getTag(), UNKNOWN).handle(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException("PluginMessage handling exception: " + event + "\n" + Arrays.toString(event.getData()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(String channel, boolean clientSideRegister, Parser handler) {
|
||||||
|
handlers.put(channel, handler);
|
||||||
|
if(clientSideRegister)
|
||||||
|
ProxyServer.getInstance().registerChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clientRegistersChannel(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
|
||||||
|
|
||||||
|
for(String channel : new String(event.getData()).split("\0")) {
|
||||||
|
if(channel.equals(ApolloManager.PLUGIN_MESSAGE_CHANNEL))
|
||||||
|
lunar.sendRestrictions(player);
|
||||||
|
|
||||||
|
if(!knownChannels.contains(channel))
|
||||||
|
BungeeCore.get().getLogger().log(Level.WARNING, () -> player.getName() + " registered unknown channel " + channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
PASS_THROUGH.handle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void serverRegistersChannel(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer)event.getReceiver();
|
||||||
|
|
||||||
|
List<String> channels = new ArrayList<>(Arrays.asList(new String(event.getData()).split("\0")));
|
||||||
|
channels.removeIf(channel -> channel.equals("sw:bridge"));
|
||||||
|
player.sendData((player).getPendingConnection().getVersion() > 340 ? "minecraft:register" : "REGISTER", String.join("\0", channels).getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void userBrand(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(event.getData());
|
||||||
|
String brand = DefinedPacket.readString(buf);
|
||||||
|
|
||||||
|
if(brand.startsWith("lunarclient:")) {
|
||||||
|
lunar.sendRestrictions(player);
|
||||||
|
} else if(!knownBrands.contains(brand)) {
|
||||||
|
BungeeCore.get().getLogger().log(Level.WARNING, () -> player.getName() + " joined with unknown brand " + brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
PASS_THROUGH.handle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void steamWarBrand(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getReceiver();
|
||||||
|
String brandString = ChatSender.of(player).parseToLegacy("STEAMWAR_BRAND", ProxyServer.getInstance().getName(), player.getServer().getInfo().getName(), new String(event.getData(), 1, event.getData().length - 1));
|
||||||
|
|
||||||
|
ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||||
|
DefinedPacket.writeString(brandString, brand);
|
||||||
|
player.sendData(event.getTag(), DefinedPacket.toArray(brand));
|
||||||
|
brand.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Parser directional(Parser fromServer, Parser fromPlayer) {
|
||||||
|
return event -> {
|
||||||
|
if(event.getSender() instanceof ProxiedPlayer)
|
||||||
|
fromPlayer.handle(event);
|
||||||
|
else
|
||||||
|
fromServer.handle(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private Parser onlySWSource(Parser parser) {
|
||||||
|
return event -> {
|
||||||
|
Connection sender = event.getSender();
|
||||||
|
if(TeamCommand.isLocalhost(sender instanceof ProxiedPlayer ? IPSanitizer.getTrueAddress(((ProxiedPlayer) sender).getPendingConnection()) : sender.getAddress().getAddress()))
|
||||||
|
parser.handle(event);
|
||||||
|
else
|
||||||
|
UNKNOWN.handle(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parser async(Parser parser) {
|
||||||
|
return event -> ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> parser.handle(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Parser {
|
||||||
|
void handle(PluginMessageEvent event);
|
||||||
|
}
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners;
|
|
||||||
|
|
||||||
import net.md_5.bungee.BungeeCord;
|
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
|
||||||
import net.md_5.bungee.connection.InitialHandler;
|
|
||||||
import net.md_5.bungee.connection.LoginResult;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
import net.md_5.bungee.protocol.Property;
|
|
||||||
import net.md_5.bungee.util.AddressUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class SubserverProtocolFixer extends BasicListener {
|
|
||||||
|
|
||||||
private final InetSocketAddress inetSocketAddress = new InetSocketAddress("127.127.127.127", 25565);
|
|
||||||
|
|
||||||
private Field field;
|
|
||||||
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
field = InitialHandler.class.getDeclaredField("extraDataInHandshake");
|
|
||||||
field.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
BungeeCord.getInstance().getLogger().log(Level.SEVERE, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void loginEvent(LoginEvent e) {
|
|
||||||
InitialHandler initialHandler = ((InitialHandler) e.getConnection());
|
|
||||||
|
|
||||||
String undashedUUID = initialHandler.getUniqueId().toString().replace("-", "");
|
|
||||||
String extraData = "\00" + AddressUtil.sanitizeAddress(inetSocketAddress) + "\00" + undashedUUID;
|
|
||||||
|
|
||||||
LoginResult result = initialHandler.getLoginProfile();
|
|
||||||
if (result != null) {
|
|
||||||
Property[] properties = result.getProperties();
|
|
||||||
if (properties.length > 0) {
|
|
||||||
extraData += "\00" + BungeeCord.getInstance().gson.toJson(properties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
field.set(initialHandler, extraData);
|
|
||||||
} catch (IllegalAccessException ex) {
|
|
||||||
BungeeCord.getInstance().getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
public class Badlion extends BasicListener {
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPostLogin(PostLoginEvent event) {
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"modsDisallowed": {
|
|
||||||
"Clear Glass":{"disabled":true},
|
|
||||||
"ClearWater":{"disabled":true},
|
|
||||||
"FOV Changer":{"disabled":true},
|
|
||||||
"Hitboxes":{"disabled":true},
|
|
||||||
"MiniMap":{"disabled":true},
|
|
||||||
"MLG Cobweb":{"disabled":true},
|
|
||||||
"Replay":{"disabled":true},
|
|
||||||
"Schematica":{"disabled":true},
|
|
||||||
"ToggleSneak":{"disabled":true},
|
|
||||||
"ToggleSprint":{"disabled":true},
|
|
||||||
"TNT Time":{"disabled":true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
event.getPlayer().sendData("badlion:mods", ("{\"Clear Glass\":{\"disabled\":true},\"ClearWater\":{\"disabled\":true},\"FOV Changer\":{\"disabled\":true},\"Hitboxes\":{\"disabled\":true},\"LevelHead\":{\"disabled\":true},\"MiniMap\":{\"disabled\":true},\"MLG Cobweb\":{\"disabled\":true},\"Replay\":{\"disabled\":true},\"Schematica\":{\"disabled\":true},\"ToggleSneak\":{\"disabled\":true},\"ToggleSprint\":{\"disabled\":true},\"TNT Time\":{\"disabled\":true}}").getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.Storage;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.Mod;
|
|
||||||
import de.steamwar.sql.SWException;
|
|
||||||
import de.steamwar.sql.SteamwarUser;
|
|
||||||
import net.md_5.bungee.BungeeCord;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class Fabric extends BasicListener {
|
|
||||||
|
|
||||||
public static void remove(ProxiedPlayer player) {
|
|
||||||
Storage.fabricCheckedPlayers.remove(player);
|
|
||||||
synchronized (Storage.fabricExpectPluginMessage) {
|
|
||||||
Storage.fabricExpectPluginMessage.remove(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final HashSet<String> ppCircumventerList = new HashSet<>();
|
|
||||||
static {
|
|
||||||
ppCircumventerList.add("java");
|
|
||||||
ppCircumventerList.add("minecraft");
|
|
||||||
ppCircumventerList.add("org_joml_joml");
|
|
||||||
ppCircumventerList.add("steamwarmodsender");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Set<String> neededMods = new HashSet<>();
|
|
||||||
static {
|
|
||||||
neededMods.add("java");
|
|
||||||
neededMods.add("minecraft");
|
|
||||||
neededMods.add("fabricloader");
|
|
||||||
neededMods.add("steamwarmodsender");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
|
||||||
synchronized (Storage.fabricExpectPluginMessage) {
|
|
||||||
for (Map.Entry<ProxiedPlayer, Long> entry : Storage.fabricExpectPluginMessage.entrySet()) {
|
|
||||||
if (!Storage.fabricCheckedPlayers.containsKey(entry.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (System.currentTimeMillis() - entry.getValue() > TimeUnit.SECONDS.toMillis(20)) {
|
|
||||||
logMessage(SteamwarUser.get(entry.getKey().getUniqueId()), "Expected message not received", String.valueOf(entry.getValue()));
|
|
||||||
Storage.fabricExpectPluginMessage.remove(entry.getKey());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, 1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginMessageEvent(PluginMessageEvent e){
|
|
||||||
if(!e.getTag().equals("fabricmodsender:mods"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!(e.getSender() instanceof ProxiedPlayer)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxiedPlayer player = (ProxiedPlayer) e.getSender();
|
|
||||||
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
|
||||||
byte[] data = e.getData();
|
|
||||||
|
|
||||||
if (!Storage.fabricCheckedPlayers.containsKey(player)) {
|
|
||||||
synchronized (Storage.fabricExpectPluginMessage) {
|
|
||||||
if (Storage.fabricExpectPluginMessage.containsKey(player)) {
|
|
||||||
logMessage(user, "Was not fabric checked but send message nonetheless", Arrays.toString(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Storage.fabricExpectPluginMessage.remove(player);
|
|
||||||
|
|
||||||
List<Mod> mods = new ArrayList<>();
|
|
||||||
|
|
||||||
Utils.VarInt varInt = Utils.readVarInt(data,0);
|
|
||||||
|
|
||||||
if(data.length != varInt.length + varInt.value) {
|
|
||||||
logMessage(user, "Invalid message length", Arrays.toString(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = Arrays.copyOfRange(data,varInt.length, data.length);
|
|
||||||
|
|
||||||
String dataString;
|
|
||||||
|
|
||||||
try{
|
|
||||||
dataString = new String(data, StandardCharsets.UTF_8);
|
|
||||||
}catch (UnsupportedCharsetException exception) {
|
|
||||||
logMessage(user, "Unsupported charset", Arrays.toString(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonArray array;
|
|
||||||
|
|
||||||
try {
|
|
||||||
array = JsonParser.parseString(dataString).getAsJsonArray();
|
|
||||||
}catch (JsonSyntaxException exception) {
|
|
||||||
logMessage(user, "Invalid json", dataString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(JsonElement mod : array) {
|
|
||||||
mods.add(Mod.getOrCreate(mod.getAsString(), Mod.Platform.FABRIC));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean neededMods = neededModsContained(mods);
|
|
||||||
if(!neededMods) {
|
|
||||||
logMessage(user, "Needed mods are not contained", dataString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ppCircumventerCheck(mods))
|
|
||||||
logMessage(user, "PP circumventer suspicion", dataString);
|
|
||||||
|
|
||||||
if(!Utils.handleMods(player,mods))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Storage.fabricCheckedPlayers.containsKey(player)) {
|
|
||||||
long current = Storage.fabricCheckedPlayers.get(player);
|
|
||||||
if (current != dataString.hashCode()) {
|
|
||||||
logMessage(user, "Mods changed during runtime", dataString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Storage.fabricCheckedPlayers.put(player, dataString.hashCode());
|
|
||||||
}
|
|
||||||
Storage.fabricPlayers.remove(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onServerSwitchEvent(ServerSwitchEvent e) {
|
|
||||||
if (e.getFrom() == null) return;
|
|
||||||
synchronized (Storage.fabricExpectPluginMessage) {
|
|
||||||
Storage.fabricExpectPluginMessage.put(e.getPlayer(), System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean neededModsContained(List<Mod> mods) {
|
|
||||||
return mods.stream()
|
|
||||||
.map(Mod::getModName)
|
|
||||||
.filter(neededMods::contains)
|
|
||||||
.count() == neededMods.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logMessage(SteamwarUser user, String reason, String data) {
|
|
||||||
SWException.log("FabricModSender " + user.getUserName() + ": " + reason, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean ppCircumventerCheck(List<Mod> mods) {
|
|
||||||
for(Mod mod : mods) {
|
|
||||||
String name = mod.getModName();
|
|
||||||
if(!name.startsWith("fabric") && !ppCircumventerList.contains(name))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.Mod;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
|
||||||
import net.md_5.bungee.connection.InitialHandler;
|
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
|
||||||
import net.md_5.bungee.netty.HandlerBoss;
|
|
||||||
import net.md_5.bungee.netty.PacketHandler;
|
|
||||||
import net.md_5.bungee.protocol.packet.LoginPayloadRequest;
|
|
||||||
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class Forge extends BasicListener {
|
|
||||||
|
|
||||||
private static final String WRAPPER = "fml:loginwrapper";
|
|
||||||
|
|
||||||
private static final Field initialHandlerCh;
|
|
||||||
static{
|
|
||||||
try {
|
|
||||||
initialHandlerCh = InitialHandler.class.getDeclaredField("ch");
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
throw new SecurityException("Could not initialize Reflection", e);
|
|
||||||
}
|
|
||||||
initialHandlerCh.setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onServerConnected(LoginEvent event) {
|
|
||||||
if(event.getConnection().getVersion() < 341 || event.getConnection().getVersion() > 763) { //1.13 - 1.20.1
|
|
||||||
event.completeIntent(BungeeCore.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//fml:handshake without mods, channels and registries
|
|
||||||
//for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
|
|
||||||
event.getConnection().unsafe().sendPacket(new LoginPayloadRequest(1, WRAPPER, new byte[]{13,102,109,108,58,104,97,110,100,115,104,97,107,101,4,1,0,0,0}));
|
|
||||||
|
|
||||||
InitialHandler handler = (InitialHandler) event.getConnection();
|
|
||||||
|
|
||||||
ChannelWrapper wrapper;
|
|
||||||
try{
|
|
||||||
wrapper = (ChannelWrapper) initialHandlerCh.get(handler);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e);
|
|
||||||
event.completeIntent(BungeeCore.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelPipeline pipeline = wrapper.getHandle().pipeline();
|
|
||||||
if(pipeline != null) {
|
|
||||||
HandlerBoss handlerBoss = pipeline.get(HandlerBoss.class);
|
|
||||||
if(handlerBoss != null)
|
|
||||||
handlerBoss.setHandler(new CustomPacketHandler(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CustomPacketHandler extends PacketHandler {
|
|
||||||
private final LoginEvent event;
|
|
||||||
|
|
||||||
public CustomPacketHandler(LoginEvent event) {
|
|
||||||
this.event = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SteamWar Forge Handler";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(LoginPayloadResponse response){
|
|
||||||
byte[] data = response.getData();
|
|
||||||
if(data == null) {
|
|
||||||
event.completeIntent(BungeeCore.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
|
|
||||||
Utils.VarInt channelLength = Utils.readVarInt(data, 0);
|
|
||||||
int pos = channelLength.length;
|
|
||||||
if(!new String(data, pos, channelLength.value).equals("fml:handshake")) {
|
|
||||||
event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x00)"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pos += channelLength.value;
|
|
||||||
|
|
||||||
Utils.VarInt length = Utils.readVarInt(data, pos);
|
|
||||||
pos += length.length;
|
|
||||||
if(channelLength.length + channelLength.value + length.length + length.value != data.length) {
|
|
||||||
event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x01)"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.VarInt packetId = Utils.readVarInt(data, pos);
|
|
||||||
pos += packetId.length;
|
|
||||||
if(packetId.value != 2) {
|
|
||||||
event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x02)"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.VarInt modCount = Utils.readVarInt(data, pos);
|
|
||||||
pos += modCount.length;
|
|
||||||
|
|
||||||
List<Mod> mods = new ArrayList<>();
|
|
||||||
for(int i = 0; i < modCount.value; i++) {
|
|
||||||
Utils.VarInt nameLength = Utils.readVarInt(data, pos);
|
|
||||||
pos += nameLength.length;
|
|
||||||
|
|
||||||
mods.add(Mod.getOrCreate(new String(data, pos, nameLength.value), Mod.Platform.FORGE));
|
|
||||||
pos += nameLength.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingConnection connection = event.getConnection();
|
|
||||||
if(!Utils.handleMods(connection.getUniqueId(), Locale.getDefault(), event::setCancelReason, mods)) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
event.completeIntent(BungeeCore.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.Mod;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
import net.md_5.bungee.api.connection.Connection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class Forge12 extends BasicListener {
|
|
||||||
private static final String FMLHS = "FML|HS";
|
|
||||||
private static final byte[] REGISTER;
|
|
||||||
private static final byte[] HELLO = new byte[]{0, 2, 0, 0, 0, 0};
|
|
||||||
|
|
||||||
private static final Set<UUID> unlocked = new HashSet<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.directBuffer(7);
|
|
||||||
buf.writeByte(6);
|
|
||||||
buf.writeCharSequence(FMLHS, StandardCharsets.UTF_8);
|
|
||||||
REGISTER = new byte[buf.readableBytes()];
|
|
||||||
buf.readBytes(REGISTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPostLogin(PostLoginEvent event) {
|
|
||||||
ProxiedPlayer player = event.getPlayer();
|
|
||||||
|
|
||||||
synchronized (unlocked) {
|
|
||||||
if(unlocked.contains(player.getUniqueId())){
|
|
||||||
unlocked.remove(player.getUniqueId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(player.getPendingConnection().getVersion() <= 340) {
|
|
||||||
player.sendData("REGISTER", REGISTER); //1.12-
|
|
||||||
player.sendData(FMLHS, HELLO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginMessageEvent(PluginMessageEvent e){
|
|
||||||
if(!e.getTag().equals(FMLHS))
|
|
||||||
return;
|
|
||||||
|
|
||||||
e.setCancelled(true);
|
|
||||||
byte[] data = e.getData();
|
|
||||||
|
|
||||||
Connection sender = e.getSender();
|
|
||||||
if(!(sender instanceof ProxiedPlayer))
|
|
||||||
return;
|
|
||||||
ProxiedPlayer p = (ProxiedPlayer) sender;
|
|
||||||
|
|
||||||
if (data[0] == 2) {
|
|
||||||
Utils.VarInt numMods = Utils.readVarInt(data, 1);
|
|
||||||
List<Mod> mods = new LinkedList<>();
|
|
||||||
|
|
||||||
int bytePos = 1 + numMods.length;
|
|
||||||
for (int i = 0; i < numMods.value; i++) {
|
|
||||||
byte[] name = Arrays.copyOfRange(data, bytePos + 1, bytePos + data[bytePos] + 1);
|
|
||||||
bytePos += 1 + data[bytePos];
|
|
||||||
//Version information is unused
|
|
||||||
bytePos += 1 + data[bytePos];
|
|
||||||
|
|
||||||
mods.add(Mod.getOrCreate(new String(name), Mod.Platform.FORGE));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils.handleMods(p, mods)) {
|
|
||||||
synchronized (unlocked) {
|
|
||||||
unlocked.add(p.getUniqueId());
|
|
||||||
}
|
|
||||||
ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(),
|
|
||||||
() -> p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten")),
|
|
||||||
2, TimeUnit.SECONDS);
|
|
||||||
ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
|
||||||
synchronized (unlocked) {
|
|
||||||
unlocked.remove(p.getUniqueId());
|
|
||||||
}
|
|
||||||
}, 30, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.Mod;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
import net.md_5.bungee.api.connection.Connection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class LabyMod extends BasicListener {
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginMessageEvent(PluginMessageEvent event){
|
|
||||||
if(!event.getTag().equals("LMC"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Connection sender = event.getSender();
|
|
||||||
if(!(sender instanceof ProxiedPlayer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
event.setCancelled(true);
|
|
||||||
updateGameInfo((ProxiedPlayer) sender);
|
|
||||||
byte[] data = event.getData();
|
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> runAsync(data, (ProxiedPlayer) sender));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runAsync(byte[] data, ProxiedPlayer player) {
|
|
||||||
VarString purpose = readString(data, 0);
|
|
||||||
if(!"INFO".equals(purpose.value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
VarString value = readString(data, purpose.length);
|
|
||||||
List<Mod> mods = new LinkedList<>();
|
|
||||||
|
|
||||||
try{
|
|
||||||
InfoPacket info = new InfoPacket(value.value);
|
|
||||||
for(InfoPacket.Addon addon : info.addons) {
|
|
||||||
mods.add(Mod.getOrCreate(addon.name, Mod.Platform.LABYMOD));
|
|
||||||
}
|
|
||||||
}catch(IOException e){
|
|
||||||
BungeeCore.log("Could not read JSON", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.handleMods(player, mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VarString readString(byte[] array, int startPos){
|
|
||||||
Utils.VarInt varInt = Utils.readVarInt(array, startPos);
|
|
||||||
startPos += varInt.length;
|
|
||||||
return new VarString(varInt.value+varInt.length, new String(Arrays.copyOfRange(array, startPos, startPos + varInt.value), StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGameInfo(ProxiedPlayer proxiedPlayer) {
|
|
||||||
JsonObject obj = new JsonObject();
|
|
||||||
obj.addProperty("hasGame", true);
|
|
||||||
obj.addProperty("game_mode", "steamwar.de");
|
|
||||||
obj.addProperty("game_startTime", 0);
|
|
||||||
obj.addProperty("game_endTime", 0);
|
|
||||||
String output = "{ \"hasGame\" : \"true\", \"game_mode\" : \"steamwar.de\", \"game_startTime\" : \"0\", \"game_endTime\" : \"0\" }";
|
|
||||||
|
|
||||||
proxiedPlayer.sendData("LMC", output.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class VarString{
|
|
||||||
private final int length;
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private VarString(int length, String value) {
|
|
||||||
this.length = length;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class InfoPacket{
|
|
||||||
String version = null;
|
|
||||||
Feature ccp = null;
|
|
||||||
Feature shadow = null;
|
|
||||||
List<Addon> addons = new LinkedList<>();
|
|
||||||
|
|
||||||
InfoPacket(String input) throws IOException {
|
|
||||||
JsonReader reader = new JsonReader(new StringReader(input));
|
|
||||||
reader.beginObject();
|
|
||||||
while(reader.hasNext()){
|
|
||||||
String name = reader.nextName();
|
|
||||||
switch(name){
|
|
||||||
case "version":
|
|
||||||
version = reader.nextString();
|
|
||||||
break;
|
|
||||||
case "ccp":
|
|
||||||
ccp = new Feature(reader);
|
|
||||||
break;
|
|
||||||
case "shadow":
|
|
||||||
shadow = new Feature(reader);
|
|
||||||
break;
|
|
||||||
case "addons":
|
|
||||||
reader.beginArray();
|
|
||||||
while(reader.hasNext()){
|
|
||||||
addons.add(new Addon(reader));
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Addon{
|
|
||||||
UUID uuid = null;
|
|
||||||
String name = null;
|
|
||||||
|
|
||||||
Addon(JsonReader reader) throws IOException {
|
|
||||||
reader.beginObject();
|
|
||||||
while(reader.hasNext()){
|
|
||||||
String n = reader.nextName();
|
|
||||||
if(n.equals("uuid"))
|
|
||||||
try{
|
|
||||||
uuid = UUID.fromString(reader.nextString());
|
|
||||||
}catch(IllegalArgumentException ignored){
|
|
||||||
//ignored
|
|
||||||
}
|
|
||||||
else if(n.equals("name"))
|
|
||||||
name = reader.nextString();
|
|
||||||
else
|
|
||||||
reader.skipValue();
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Feature{
|
|
||||||
boolean enabled;
|
|
||||||
int version;
|
|
||||||
|
|
||||||
Feature(JsonReader reader) throws IOException {
|
|
||||||
reader.beginObject();
|
|
||||||
while(reader.hasNext()){
|
|
||||||
String name = reader.nextName();
|
|
||||||
if(name.equals("version"))
|
|
||||||
version = reader.nextInt();
|
|
||||||
else if(name.equals("enabled"))
|
|
||||||
enabled = reader.nextBoolean();
|
|
||||||
else
|
|
||||||
reader.skipValue();
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.*;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import net.md_5.bungee.api.connection.Connection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ModLoaderBlocker extends BasicListener {
|
|
||||||
|
|
||||||
private static final Set<String> BLOCKED_SERVER = new HashSet<>();
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginMessageEvent(PluginMessageEvent e){
|
|
||||||
Connection sender = e.getSender();
|
|
||||||
if(!(sender instanceof ProxiedPlayer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
ProxiedPlayer p = (ProxiedPlayer) sender;
|
|
||||||
if (e.getTag().equals("lunarclient:pm")) {
|
|
||||||
p.disconnect(Message.parseToComponent("MOD_LOADER_LUNAR_CLIENT", false, p));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!e.getTag().equals("minecraft:brand"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
String brand = new String(e.getData());
|
|
||||||
if(brand.contains("fabric") || brand.contains("quilt") || brand.contains("LiteLoader")){
|
|
||||||
if (!Storage.fabricCheckedPlayers.containsKey(p)) {
|
|
||||||
Storage.fabricPlayers.add(p);
|
|
||||||
}
|
|
||||||
} else if (brand.contains("lunar")) {
|
|
||||||
p.disconnect(Message.parseToComponent("MOD_LOADER_LUNAR_CLIENT", false, p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onDisconnect(PlayerDisconnectEvent e){
|
|
||||||
Fabric.remove(e.getPlayer());
|
|
||||||
Storage.fabricPlayers.remove(e.getPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onServerSwitch(ServerSwitchEvent event) {
|
|
||||||
if(((Subserver.getSubserver(event.getPlayer()) != null
|
|
||||||
&& Subserver.getSubserver(event.getPlayer()).getType() == Servertype.ARENA)
|
|
||||||
|| BLOCKED_SERVER.contains(event.getPlayer().getServer().getInfo().getName()))
|
|
||||||
&& isFabric(event.getPlayer())) {
|
|
||||||
event.getPlayer().connect(BungeeCore.get().getProxy().getServerInfo(BungeeCore.LOBBY_SERVER));
|
|
||||||
Message.send("MODLOADER_DENIED", event.getPlayer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isFabric(ProxiedPlayer player) {
|
|
||||||
return Storage.fabricPlayers.contains(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addServer(String server) {
|
|
||||||
BLOCKED_SERVER.add(server);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package de.steamwar.bungeecore.listeners.mods;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.Mod;
|
|
||||||
import net.md_5.bungee.api.connection.Connection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class WorldDownloader extends BasicListener {
|
|
||||||
|
|
||||||
private static final Set<String> wdlTags = Collections.unmodifiableSet(
|
|
||||||
Sets.newHashSet("WDL|INIT", "wdl:init", "WDL|REQUEST", "wdl:request")
|
|
||||||
);
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginMessageEvent(PluginMessageEvent event){
|
|
||||||
if(!wdlTags.contains(event.getTag()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Connection sender = event.getSender();
|
|
||||||
if(!(sender instanceof ProxiedPlayer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
event.setCancelled(true);
|
|
||||||
Utils.handleMods((ProxiedPlayer) sender, Lists.newArrayList(Mod.getOrCreate("wdl", Mod.Platform.FORGE)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.ping;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
|
|
||||||
public class PingListener extends BasicListener {
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPing(ProxyPingEvent event) {
|
|
||||||
event.setResponse(new SteamWarServerPing(event.getResponse(), event.getConnection().getVersion()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is a part of the SteamWar software.
|
|
||||||
|
|
||||||
Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.ping;
|
|
||||||
|
|
||||||
import net.md_5.bungee.api.ServerPing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SteamWarServerPing extends ServerPing {
|
|
||||||
|
|
||||||
private final boolean preventsChatReports = true;
|
|
||||||
private final ForgeData forgeData;
|
|
||||||
|
|
||||||
public SteamWarServerPing(ServerPing existing, int version) {
|
|
||||||
super(existing.getVersion(), existing.getPlayers(), existing.getDescriptionComponent(), existing.getFaviconObject());
|
|
||||||
forgeData = new ForgeData(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ForgeData {
|
|
||||||
private final List<ForgeChannel> channels = new ArrayList<>();
|
|
||||||
private final List<ForgeMod> mods = new ArrayList<>();
|
|
||||||
private final int fmlNetworkVersion = 2;
|
|
||||||
|
|
||||||
public ForgeData(int versionNumber) {
|
|
||||||
channels.add(new ForgeChannel("minecraft:unregister"));
|
|
||||||
channels.add(new ForgeChannel("minecraft:register"));
|
|
||||||
channels.add(new ForgeChannel("fml:handshake"));
|
|
||||||
mods.add(new ForgeMod("minecraft", ProtocolVersion.getVersion(versionNumber)));
|
|
||||||
mods.add(new ForgeMod("forge", "ANY"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static class ProtocolVersion {
|
|
||||||
|
|
||||||
private static final HashMap<Integer, String> versions;
|
|
||||||
|
|
||||||
static {
|
|
||||||
versions = new HashMap();
|
|
||||||
versions.put(757, "1.18");
|
|
||||||
versions.put(756, "1.17.1");
|
|
||||||
versions.put(754, "1.16.5");
|
|
||||||
versions.put(578, "1.15.2");
|
|
||||||
versions.put(498, "1.14.1");
|
|
||||||
versions.put(393, "1.13");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getVersion(int version) {
|
|
||||||
return versions.get(version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ForgeChannel {
|
|
||||||
private final String res;
|
|
||||||
private final String version = "FML2";
|
|
||||||
private final boolean required = true;
|
|
||||||
|
|
||||||
private ForgeChannel(String res) {
|
|
||||||
this.res = res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ForgeMod {
|
|
||||||
private final String modId;
|
|
||||||
private final String modmarker;
|
|
||||||
|
|
||||||
private ForgeMod(String modId, String modmarker) {
|
|
||||||
this.modId = modId;
|
|
||||||
this.modmarker = modmarker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
src/de/steamwar/bungeecore/mods/Badlion.java
Normale Datei
57
src/de/steamwar/bungeecore/mods/Badlion.java
Normale Datei
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
|
public class Badlion extends BasicListener {
|
||||||
|
// https://github.com/BadlionClient/BadlionClientModAPI
|
||||||
|
|
||||||
|
private final byte[] packet;
|
||||||
|
|
||||||
|
public Badlion() { //TODO check if working or (json) modsDisallowed wrapper necessary
|
||||||
|
JsonObject disabled = new JsonObject();
|
||||||
|
disabled.addProperty("disabled", true);
|
||||||
|
|
||||||
|
JsonObject json = new JsonObject();
|
||||||
|
json.add("Clear Glass", disabled);
|
||||||
|
json.add("ClearWater", disabled);
|
||||||
|
json.add("FOV Changer", disabled);
|
||||||
|
json.add("Hitboxes", disabled);
|
||||||
|
json.add("LevelHead", disabled);
|
||||||
|
json.add("MiniMap", disabled);
|
||||||
|
json.add("MLG Cobweb", disabled);
|
||||||
|
json.add("Replay", disabled); //TODO check if ReplayMod restrictions work
|
||||||
|
json.add("Schematica", disabled);
|
||||||
|
json.add("ToggleSneak", disabled);
|
||||||
|
json.add("ToggleSprint", disabled);
|
||||||
|
json.add("TNT Time", disabled);
|
||||||
|
|
||||||
|
packet = json.toString().getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPostLogin(PostLoginEvent event) {
|
||||||
|
event.getPlayer().sendData("badlion:mods", packet);
|
||||||
|
}
|
||||||
|
}
|
98
src/de/steamwar/bungeecore/mods/FML.java
Normale Datei
98
src/de/steamwar/bungeecore/mods/FML.java
Normale Datei
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import de.steamwar.sql.Mod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.protocol.DefinedPacket;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class FML extends BasicListener {
|
||||||
|
// https://wiki.vg/Minecraft_Forge_Handshake#FML_protocol_.281.7_-_1.12.29
|
||||||
|
|
||||||
|
public static boolean isFML(PendingConnection connection, String type) {
|
||||||
|
return ((InitialHandler)connection).getExtraDataInHandshake().equals("\0" + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String CHANNEL = "FML|HS";
|
||||||
|
private final byte[] helloPacket = new byte[]{
|
||||||
|
/* Packet type: ServerHello */ 0,
|
||||||
|
/* FML protocol version */ 2,
|
||||||
|
/* Override dimension (int) */ 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Set<UUID> unlocked = new HashSet<>();
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPostLogin(PostLoginEvent event) {
|
||||||
|
ProxiedPlayer player = event.getPlayer();
|
||||||
|
|
||||||
|
synchronized (unlocked) {
|
||||||
|
if(unlocked.contains(player.getUniqueId())){
|
||||||
|
unlocked.remove(player.getUniqueId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isFML(player.getPendingConnection(), "FML\0"))
|
||||||
|
player.sendData(CHANNEL, helloPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePluginMessage(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer p = (ProxiedPlayer) event.getSender();
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(event.getData());
|
||||||
|
|
||||||
|
if (buf.readByte() == /* ModList */ 2) {
|
||||||
|
int numMods = DefinedPacket.readVarInt(buf);
|
||||||
|
|
||||||
|
List<Mod> mods = new ArrayList<>();
|
||||||
|
for(int i = 0; i < numMods; i++) {
|
||||||
|
String name = DefinedPacket.readString(buf);
|
||||||
|
DefinedPacket.readString(buf); // version
|
||||||
|
|
||||||
|
mods.add(Mod.getOrCreate(name, Mod.Platform.FORGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModUtils.handleMods(p, mods)) {
|
||||||
|
synchronized (unlocked) {
|
||||||
|
unlocked.add(p.getUniqueId());
|
||||||
|
}
|
||||||
|
p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten"));
|
||||||
|
ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
||||||
|
synchronized (unlocked) {
|
||||||
|
unlocked.remove(p.getUniqueId());
|
||||||
|
}
|
||||||
|
}, 30, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
186
src/de/steamwar/bungeecore/mods/FML2.java
Normale Datei
186
src/de/steamwar/bungeecore/mods/FML2.java
Normale Datei
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import de.steamwar.bungeecore.listeners.IPSanitizer;
|
||||||
|
import de.steamwar.sql.Mod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.netty.HandlerBoss;
|
||||||
|
import net.md_5.bungee.netty.PacketHandler;
|
||||||
|
import net.md_5.bungee.protocol.DefinedPacket;
|
||||||
|
import net.md_5.bungee.protocol.packet.LoginPayloadRequest;
|
||||||
|
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class FML2 extends BasicListener {
|
||||||
|
// FML2: https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
|
||||||
|
// FML3: https://github.com/adde0109/Ambassador/tree/non-api/src/main/java/org/adde0109/ambassador/forge
|
||||||
|
|
||||||
|
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkInitialization.java
|
||||||
|
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ForgePacketHandler.java
|
||||||
|
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/packets/ModVersions.java
|
||||||
|
|
||||||
|
private final byte[] fml2ModListPacket;
|
||||||
|
private final byte[] fml3ModListPacket;
|
||||||
|
private final byte[] forgeModListPacket;
|
||||||
|
|
||||||
|
public FML2() {
|
||||||
|
fml2ModListPacket = generateModListPacket(false);
|
||||||
|
fml3ModListPacket = generateModListPacket(true);
|
||||||
|
|
||||||
|
ByteBuf packet = Unpooled.buffer();
|
||||||
|
packet.writeByte(1); // Mod list packet
|
||||||
|
DefinedPacket.writeVarInt(0, packet); // Mod amount
|
||||||
|
|
||||||
|
ByteBuf wrapper = Unpooled.buffer();
|
||||||
|
wrapper.writeByte(0); // Login wrapper packet
|
||||||
|
DefinedPacket.writeString("forge:handshake", wrapper);
|
||||||
|
DefinedPacket.writeVarInt(packet.readableBytes(), wrapper);
|
||||||
|
wrapper.writeBytes(packet);
|
||||||
|
|
||||||
|
forgeModListPacket = new byte[wrapper.readableBytes()];
|
||||||
|
wrapper.readBytes(forgeModListPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onLogin(LoginEvent event) {
|
||||||
|
PendingConnection connection = event.getConnection();
|
||||||
|
|
||||||
|
boolean fml2 = FML.isFML(connection, "FML2\0");
|
||||||
|
boolean fml3 = FML.isFML(connection, "FML3\0");
|
||||||
|
boolean forge = FML.isFML(connection, "FORGE");
|
||||||
|
if(!fml2 && !fml3 && !forge)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IPSanitizer.getChannelWrapper(connection).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event));
|
||||||
|
|
||||||
|
event.registerIntent(BungeeCore.get());
|
||||||
|
if(forge)
|
||||||
|
connection.unsafe().sendPacket(new LoginPayloadRequest(1, "forge:login", forgeModListPacket));
|
||||||
|
else
|
||||||
|
connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", fml3 ? fml3ModListPacket : fml2ModListPacket));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] generateModListPacket(boolean fml3) {
|
||||||
|
ByteBuf packet = Unpooled.buffer();
|
||||||
|
packet.writeByte(1); // Mod list packet
|
||||||
|
DefinedPacket.writeVarInt(0, packet); // Mod amount
|
||||||
|
|
||||||
|
if(fml3) {
|
||||||
|
DefinedPacket.writeVarInt(1, packet); // Channel amount
|
||||||
|
DefinedPacket.writeString("forge:tier_sorting", packet);
|
||||||
|
DefinedPacket.writeString("1.0", packet);
|
||||||
|
} else {
|
||||||
|
DefinedPacket.writeVarInt(0, packet); // Channel amount
|
||||||
|
}
|
||||||
|
|
||||||
|
DefinedPacket.writeVarInt(0, packet); // Registries amount
|
||||||
|
if(fml3)
|
||||||
|
DefinedPacket.writeVarInt(0, packet); // DataPacks amount
|
||||||
|
|
||||||
|
ByteBuf wrapper = Unpooled.buffer();
|
||||||
|
DefinedPacket.writeString("fml:handshake", wrapper);
|
||||||
|
DefinedPacket.writeVarInt(packet.readableBytes(), wrapper);
|
||||||
|
wrapper.writeBytes(packet);
|
||||||
|
|
||||||
|
byte[] data = new byte[wrapper.readableBytes()];
|
||||||
|
wrapper.readBytes(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FML2LoginHandler extends PacketHandler {
|
||||||
|
private final LoginEvent event;
|
||||||
|
|
||||||
|
public FML2LoginHandler(LoginEvent event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SteamWar Forge Handler";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(LoginPayloadResponse response) {
|
||||||
|
boolean forge = FML.isFML(event.getConnection(), "FORGE");
|
||||||
|
byte[] data = response.getData();
|
||||||
|
if(data == null) {
|
||||||
|
abort(response, "Not FML2/3 client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(data);
|
||||||
|
if(forge && buf.readByte() != 0) {
|
||||||
|
abort(response, "Not FORGE login wrapper");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!DefinedPacket.readString(buf).equals(forge ? "forge:handshake" : "fml:handshake")) {
|
||||||
|
abort(response, "Not FML2/3/FORGE handshake response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DefinedPacket.readVarInt(buf) != buf.readableBytes()) {
|
||||||
|
abort(response, "FML2/3/FORGE packet size mismatch");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(DefinedPacket.readVarInt(buf) != (forge ? /* Mod Versions */ 1 : /* Mod List Reply */ 2)) {
|
||||||
|
abort(response, "Not FML2/3/FORGE mod list reply");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Mod> mods = new ArrayList<>();
|
||||||
|
|
||||||
|
int modCount = DefinedPacket.readVarInt(buf);
|
||||||
|
for(int i = 0; i < modCount; i++) {
|
||||||
|
mods.add(Mod.getOrCreate(DefinedPacket.readString(buf), Mod.Platform.FORGE));
|
||||||
|
|
||||||
|
if(forge) {
|
||||||
|
DefinedPacket.readString(buf); // Human readable name
|
||||||
|
DefinedPacket.readString(buf); // Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ModUtils.handleMods(event.getConnection().getUniqueId(), Locale.getDefault(), event::setReason, mods))
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
event.completeIntent(BungeeCore.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void abort(LoginPayloadResponse response, String error) {
|
||||||
|
event.setReason(TextComponent.fromLegacy(error));
|
||||||
|
event.setCancelled(true);
|
||||||
|
event.completeIntent(BungeeCore.get());
|
||||||
|
BungeeCore.get().getLogger().log(Level.SEVERE, () -> error + "\n" + response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
src/de/steamwar/bungeecore/mods/FabricModSender.java
Normale Datei
141
src/de/steamwar/bungeecore/mods/FabricModSender.java
Normale Datei
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.Storage;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import de.steamwar.sql.Mod;
|
||||||
|
import de.steamwar.sql.SWException;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.md_5.bungee.BungeeCord;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.protocol.DefinedPacket;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class FabricModSender extends BasicListener {
|
||||||
|
|
||||||
|
private final Set<String> neededMods = new HashSet<>();
|
||||||
|
|
||||||
|
public FabricModSender() {
|
||||||
|
neededMods.add("java");
|
||||||
|
neededMods.add("minecraft");
|
||||||
|
neededMods.add("fabricloader");
|
||||||
|
neededMods.add("steamwarmodsender");
|
||||||
|
|
||||||
|
BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
|
||||||
|
synchronized (Storage.fabricExpectPluginMessage) {
|
||||||
|
for (Map.Entry<ProxiedPlayer, Long> entry : Storage.fabricExpectPluginMessage.entrySet()) {
|
||||||
|
if (!Storage.fabricCheckedPlayers.containsKey(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (System.currentTimeMillis() - entry.getValue() > TimeUnit.SECONDS.toMillis(20)) {
|
||||||
|
Storage.fabricExpectPluginMessage.remove(entry.getKey());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePluginMessage(PluginMessageEvent e){
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) e.getSender();
|
||||||
|
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
||||||
|
|
||||||
|
if (!Storage.fabricCheckedPlayers.containsKey(player)) {
|
||||||
|
synchronized (Storage.fabricExpectPluginMessage) {
|
||||||
|
if (Storage.fabricExpectPluginMessage.containsKey(player)) {
|
||||||
|
logMessage(user, "Was not fabric checked but send message nonetheless", Arrays.toString(e.getData()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Storage.fabricExpectPluginMessage.remove(player);
|
||||||
|
|
||||||
|
List<Mod> mods = new ArrayList<>();
|
||||||
|
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(e.getData());
|
||||||
|
String data = DefinedPacket.readString(buf, 1024*1024);
|
||||||
|
if(buf.readableBytes() > 0) {
|
||||||
|
logMessage(user, "Invalid message length", Arrays.toString(e.getData()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray array = JsonParser.parseString(data).getAsJsonArray();
|
||||||
|
|
||||||
|
for(JsonElement mod : array) {
|
||||||
|
mods.add(Mod.getOrCreate(mod.getAsString(), Mod.Platform.FABRIC));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!neededModsContained(mods)) {
|
||||||
|
logMessage(user, "Needed mods are not contained", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ModUtils.handleMods(player,mods))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Storage.fabricCheckedPlayers.containsKey(player)) {
|
||||||
|
Storage.fabricCheckedPlayers.put(player, data.hashCode());
|
||||||
|
} else if (Storage.fabricCheckedPlayers.get(player) != data.hashCode()) {
|
||||||
|
logMessage(user, "Mods changed during runtime", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onServerSwitchEvent(ServerSwitchEvent e) {
|
||||||
|
if (e.getFrom() == null) return;
|
||||||
|
synchronized (Storage.fabricExpectPluginMessage) {
|
||||||
|
Storage.fabricExpectPluginMessage.put(e.getPlayer(), System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onDisconnect(PlayerDisconnectEvent e) {
|
||||||
|
ProxiedPlayer player = e.getPlayer();
|
||||||
|
|
||||||
|
Storage.fabricCheckedPlayers.remove(player);
|
||||||
|
synchronized (Storage.fabricExpectPluginMessage) {
|
||||||
|
Storage.fabricExpectPluginMessage.remove(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean neededModsContained(List<Mod> mods) {
|
||||||
|
return mods.stream()
|
||||||
|
.map(Mod::getModName)
|
||||||
|
.filter(neededMods::contains)
|
||||||
|
.count() == neededMods.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logMessage(SteamwarUser user, String reason, String data) {
|
||||||
|
SWException.log("FabricModSender " + user.getUserName() + ": " + reason, data);
|
||||||
|
}
|
||||||
|
}
|
58
src/de/steamwar/bungeecore/mods/Hostname.java
Normale Datei
58
src/de/steamwar/bungeecore/mods/Hostname.java
Normale Datei
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import net.md_5.bungee.api.event.PlayerHandshakeEvent;
|
||||||
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class Hostname extends BasicListener {
|
||||||
|
|
||||||
|
private final Set<String> knownHostnames = new HashSet<>();
|
||||||
|
private final Set<String> knownExtraData = new HashSet<>();
|
||||||
|
|
||||||
|
public Hostname() {
|
||||||
|
knownHostnames.add("steamwar.de");
|
||||||
|
knownHostnames.add("78.31.71.136");
|
||||||
|
|
||||||
|
knownExtraData.add("");
|
||||||
|
knownExtraData.add("\0FML\0");
|
||||||
|
knownExtraData.add("\0FML2\0");
|
||||||
|
knownExtraData.add("\0FML3\0");
|
||||||
|
knownExtraData.add("\0FORGE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onHandshake(PlayerHandshakeEvent event) {
|
||||||
|
String extraDataInHandshake = ((InitialHandler) event.getConnection()).getExtraDataInHandshake();
|
||||||
|
if (!knownHostnames.contains(event.getHandshake().getHost().toLowerCase())) {
|
||||||
|
BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown hostname " + event.getHandshake() + " " + extraDataInHandshake);
|
||||||
|
} else if (!knownExtraData.contains(extraDataInHandshake)) {
|
||||||
|
BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown extra data " + event.getHandshake() + " " + extraDataInHandshake);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
src/de/steamwar/bungeecore/mods/LabyMod.java
Normale Datei
86
src/de/steamwar/bungeecore/mods/LabyMod.java
Normale Datei
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.sql.Mod;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
import net.md_5.bungee.protocol.DefinedPacket;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class LabyMod {
|
||||||
|
// https://docs.labymod.net/pages/server/introduction/
|
||||||
|
// https://github.com/LabyMod/labymod-server-api
|
||||||
|
// https://dl.labymod.net/addons.json
|
||||||
|
|
||||||
|
private final byte[] gameInfoPacket;
|
||||||
|
public LabyMod() {
|
||||||
|
ByteBuf buf = Unpooled.buffer();
|
||||||
|
DefinedPacket.writeString("discord_rpc", buf);
|
||||||
|
|
||||||
|
JsonObject json = new JsonObject();
|
||||||
|
json.addProperty("hasGame", true);
|
||||||
|
json.addProperty("game_mode", "steamwar.de");
|
||||||
|
json.addProperty("game_startTime", 0);
|
||||||
|
json.addProperty("game_endTime", 0);
|
||||||
|
DefinedPacket.writeString(json.toString(), buf);
|
||||||
|
|
||||||
|
gameInfoPacket = new byte[buf.readableBytes()];
|
||||||
|
buf.readBytes(gameInfoPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePluginMessage(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
|
||||||
|
player.sendData(event.getTag(), gameInfoPacket);
|
||||||
|
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(event.getData());
|
||||||
|
String purpose = DefinedPacket.readString(buf);
|
||||||
|
if(!"INFO".equals(purpose))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JsonObject message = JsonParser.parseString(DefinedPacket.readString(buf)).getAsJsonObject();
|
||||||
|
List<Mod> mods = new LinkedList<>();
|
||||||
|
|
||||||
|
for(JsonElement element : message.getAsJsonArray("addons")) {
|
||||||
|
JsonObject addon = element.getAsJsonObject();
|
||||||
|
mods.add(Mod.getOrCreate(addon.get("name").getAsString(), Mod.Platform.LABYMOD));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message.has("mods")) {
|
||||||
|
BungeeCore.get().getLogger().log(Level.WARNING, () -> "LabyMod External Mods for debugging: " + message.getAsJsonArray("mods"));
|
||||||
|
for(JsonElement element : message.getAsJsonArray("mods")) {
|
||||||
|
JsonObject addon = element.getAsJsonObject();
|
||||||
|
//TODO observe: FORGE and FABRIC mods available, do they always and with .jar? (would equal new mod platform)
|
||||||
|
//mods.add(Mod.getOrCreate(addon.get("name").getAsString().replace(".jar", ""), Mod.Platform.FORGE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModUtils.handleMods(player, mods);
|
||||||
|
}
|
||||||
|
}
|
132
src/de/steamwar/bungeecore/mods/Lunar.java
Normale Datei
132
src/de/steamwar/bungeecore/mods/Lunar.java
Normale Datei
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import com.lunarclient.apollo.ApolloManager;
|
||||||
|
import com.lunarclient.apollo.libs.protobuf.Any;
|
||||||
|
import com.lunarclient.apollo.libs.protobuf.InvalidProtocolBufferException;
|
||||||
|
import com.lunarclient.apollo.libs.protobuf.Message;
|
||||||
|
import com.lunarclient.apollo.mods.impl.*;
|
||||||
|
import com.lunarclient.apollo.module.ApolloModuleManager;
|
||||||
|
import com.lunarclient.apollo.module.ApolloModuleManagerImpl;
|
||||||
|
import com.lunarclient.apollo.module.modsetting.ModSettingModule;
|
||||||
|
import com.lunarclient.apollo.network.NetworkOptions;
|
||||||
|
import com.lunarclient.apollo.option.Options;
|
||||||
|
import com.lunarclient.apollo.player.AbstractApolloPlayer;
|
||||||
|
import com.lunarclient.apollo.player.v1.ModMessage;
|
||||||
|
import com.lunarclient.apollo.player.v1.PlayerHandshakeMessage;
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.sql.Mod;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class Lunar {
|
||||||
|
// https://lunarclient.dev/apollo/introduction
|
||||||
|
// https://github.com/LunarClient/Apollo
|
||||||
|
|
||||||
|
private final ApolloModuleManager manager = new ApolloModuleManagerImpl().addModule(ModSettingModule.class);
|
||||||
|
|
||||||
|
public Lunar() {
|
||||||
|
Options modSettings = manager.getModule(ModSettingModule.class).getOptions();
|
||||||
|
modSettings.set(ModReplaymod.ENABLED, false); // TODO check if restrictions working
|
||||||
|
modSettings.set(ModFreelook.ENABLED, false);
|
||||||
|
modSettings.set(ModHypixelMod.ENABLED, false);
|
||||||
|
modSettings.set(ModMinimap.ENABLED, false);
|
||||||
|
modSettings.set(ModNametag.ENABLED, false);
|
||||||
|
modSettings.set(ModTeamView.ENABLED, false);
|
||||||
|
modSettings.set(ModTntCountdown.ENABLED, false);
|
||||||
|
modSettings.set(ModToggleSneak.TOGGLE_SNEAK_CONTAINER, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendRestrictions(ProxiedPlayer player) {
|
||||||
|
NetworkOptions.sendOptions(manager.getModules(), true, new SWApolloPlayer(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePluginMessage(PluginMessageEvent event) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
|
||||||
|
Any packet;
|
||||||
|
|
||||||
|
try {
|
||||||
|
packet = Any.parseFrom(event.getData());
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle(PlayerHandshakeMessage.class, packet, handshake -> {
|
||||||
|
List<Mod> mods = new ArrayList<>();
|
||||||
|
|
||||||
|
for(ModMessage mod : handshake.getInstalledModsList()) {
|
||||||
|
switch(mod.getType()) {
|
||||||
|
case TYPE_FABRIC_INTERNAL:
|
||||||
|
case TYPE_FORGE_INTERNAL:
|
||||||
|
// Controlled with ModSettings
|
||||||
|
break;
|
||||||
|
case TYPE_FABRIC_EXTERNAL:
|
||||||
|
mods.add(Mod.getOrCreate(mod.getName(), Mod.Platform.FABRIC));
|
||||||
|
break;
|
||||||
|
case TYPE_FORGE_EXTERNAL:
|
||||||
|
mods.add(Mod.getOrCreate(mod.getName(), Mod.Platform.FORGE));
|
||||||
|
break;
|
||||||
|
case TYPE_UNSPECIFIED:
|
||||||
|
case UNRECOGNIZED:
|
||||||
|
default:
|
||||||
|
BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " uses Lunar mod with unknown type " + mod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModUtils.handleMods(player, mods);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Message> void handle(Class<T> type, Any packet, Consumer<T> handler) {
|
||||||
|
try {
|
||||||
|
handler.accept(packet.unpack(type));
|
||||||
|
} catch (InvalidProtocolBufferException ignored) { /*ignored*/ }
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class SWApolloPlayer extends AbstractApolloPlayer {
|
||||||
|
|
||||||
|
private final ProxiedPlayer player;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendPacket(Message message) {
|
||||||
|
sendPacket(Any.pack(message).toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendPacket(byte[] bytes) {
|
||||||
|
player.sendData(ApolloManager.PLUGIN_MESSAGE_CHANNEL, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public UUID getUniqueId() { return player.getUniqueId(); }
|
||||||
|
@Override public String getName() { return player.getName(); }
|
||||||
|
@Override public boolean hasPermission(String s) { return player.hasPermission(s); }
|
||||||
|
@Override public Object getPlayer() { return player; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
This file is a part of the SteamWar software.
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
Copyright (C) 2020 SteamWar.de-Serverteam
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.steamwar.bungeecore.listeners.mods;
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
import de.steamwar.bungeecore.Message;
|
import de.steamwar.bungeecore.Message;
|
||||||
@ -27,6 +27,8 @@ import de.steamwar.messages.ChatSender;
|
|||||||
import de.steamwar.sql.Mod;
|
import de.steamwar.sql.Mod;
|
||||||
import de.steamwar.sql.Mod.ModType;
|
import de.steamwar.sql.Mod.ModType;
|
||||||
import de.steamwar.sql.UserPerm;
|
import de.steamwar.sql.UserPerm;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
@ -39,35 +41,17 @@ import java.util.function.Consumer;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Utils {
|
@UtilityClass
|
||||||
|
public class ModUtils {
|
||||||
|
|
||||||
public static final Map<UUID,List<Mod>> playerModMap = new HashMap<>();
|
@Getter
|
||||||
|
private static final Map<UUID,List<Mod>> playerModMap = new HashMap<>();
|
||||||
|
|
||||||
private Utils(){}
|
public static boolean handleMods(ProxiedPlayer player, List<Mod> mods) {
|
||||||
|
|
||||||
static VarInt readVarInt(byte[] array, int startPos) {
|
|
||||||
int numRead = 0;
|
|
||||||
int result = 0;
|
|
||||||
byte read;
|
|
||||||
do {
|
|
||||||
read = array[startPos + numRead];
|
|
||||||
int value = (read & 0b01111111);
|
|
||||||
result |= (value << (7 * numRead));
|
|
||||||
|
|
||||||
numRead++;
|
|
||||||
if (numRead > 5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while ((read & 0b10000000) != 0);
|
|
||||||
|
|
||||||
return new VarInt(numRead, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean handleMods(ProxiedPlayer player, List<Mod> mods) {
|
|
||||||
return handleMods(player.getUniqueId(), ChatSender.of(player).getLocale(), player::disconnect, mods);
|
return handleMods(player.getUniqueId(), ChatSender.of(player).getLocale(), player::disconnect, mods);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean handleMods(UUID uuid, Locale locale, Consumer<BaseComponent[]> disconnect, List<Mod> mods){
|
public static boolean handleMods(UUID uuid, Locale locale, Consumer<BaseComponent> disconnect, List<Mod> mods){
|
||||||
SteamwarUser user = SteamwarUser.get(uuid);
|
SteamwarUser user = SteamwarUser.get(uuid);
|
||||||
playerModMap.put(uuid,new ArrayList<>(mods));
|
playerModMap.put(uuid,new ArrayList<>(mods));
|
||||||
|
|
||||||
@ -101,17 +85,7 @@ public class Utils {
|
|||||||
BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen der Mods " + modList + " gebannt.");
|
BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen der Mods " + modList + " gebannt.");
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect.accept(TextComponent.fromLegacyText(message));
|
disconnect.accept(TextComponent.fromLegacy(message));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class VarInt{
|
|
||||||
final int length;
|
|
||||||
final int value;
|
|
||||||
|
|
||||||
VarInt(int length, int value){
|
|
||||||
this.length = length;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
73
src/de/steamwar/bungeecore/mods/ReplayMod.java
Normale Datei
73
src/de/steamwar/bungeecore/mods/ReplayMod.java
Normale Datei
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.Bauserver;
|
||||||
|
import de.steamwar.bungeecore.Builderserver;
|
||||||
|
import de.steamwar.bungeecore.BungeeCore;
|
||||||
|
import de.steamwar.bungeecore.Subserver;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import de.steamwar.bungeecore.listeners.PluginMessage;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ReplayMod extends BasicListener {
|
||||||
|
// https://gist.github.com/Johni0702/2547c463e51f65f312cb
|
||||||
|
// https://github.com/ReplayMod/replay-restrictions/blob/master/bungeecord/src/main/java/de/johni0702/replay/restrictions/BungeeCordPlugin.java
|
||||||
|
// https://github.com/ReplayMod/ReplayMod/blob/stable/src/main/java/com/replaymod/core/utils/Restrictions.java
|
||||||
|
|
||||||
|
private final byte[] restrict;
|
||||||
|
|
||||||
|
public ReplayMod() {
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
for(String restriction : Arrays.asList("no_xray", "no_noclip", "only_first_person", "only_recording_player")) {
|
||||||
|
byte[] bytes = restriction.getBytes();
|
||||||
|
stream.write(bytes.length);
|
||||||
|
stream.write(bytes);
|
||||||
|
stream.write(1); // restrict
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
restrict = stream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(ServerSwitchEvent event) {
|
||||||
|
ProxiedPlayer player = event.getPlayer();
|
||||||
|
ServerInfo server = player.getServer().getInfo();
|
||||||
|
if(ProxyServer.getInstance().getServerInfo(BungeeCore.LOBBY_SERVER) == server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Subserver subserver = Subserver.getSubserver(server);
|
||||||
|
if(subserver instanceof Builderserver || (subserver instanceof Bauserver && ((Bauserver) subserver).getOwner().equals(player.getUniqueId())))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PluginMessage.send(player, "Replay|Restrict", "replaymod:restrict", restrict);
|
||||||
|
}
|
||||||
|
}
|
40
src/de/steamwar/bungeecore/mods/Schematica.java
Normale Datei
40
src/de/steamwar/bungeecore/mods/Schematica.java
Normale Datei
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
|
public class Schematica extends BasicListener {
|
||||||
|
// https://github.com/Lunatrius/SchematicaPlugin/blob/master/src/main/java/com/github/lunatrius/schematica/plugin/SchematicaPlugin.java
|
||||||
|
|
||||||
|
private final byte[] packet = new byte[] {
|
||||||
|
/* ProtocolVersion? */ 0,
|
||||||
|
/* PERM_PRINTER */ 1,
|
||||||
|
/* PERM_SAVE */ 0,
|
||||||
|
/* PERM_LOAD */ 1
|
||||||
|
};
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPostLogin(PostLoginEvent event) {
|
||||||
|
event.getPlayer().sendData("schematica", packet);
|
||||||
|
}
|
||||||
|
}
|
63
src/de/steamwar/bungeecore/mods/ServerListPing.java
Normale Datei
63
src/de/steamwar/bungeecore/mods/ServerListPing.java
Normale Datei
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import de.steamwar.bungeecore.listeners.BasicListener;
|
||||||
|
import net.md_5.bungee.BungeeCord;
|
||||||
|
import net.md_5.bungee.api.ServerPing;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class ServerListPing extends BasicListener implements JsonSerializer<ServerPing>, JsonDeserializer<ServerPing> {
|
||||||
|
// https://github.com/Aizistral-Studios/No-Chat-Reports/discussions/206
|
||||||
|
// https://github.com/Aizistral-Studios/No-Chat-Reports/wiki/How-to-Get-Safe-Server-Status
|
||||||
|
|
||||||
|
private final Gson gson;
|
||||||
|
|
||||||
|
public ServerListPing() {
|
||||||
|
BungeeCord bungeeCord = BungeeCord.getInstance();
|
||||||
|
gson = bungeeCord.gson;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field gsonField = BungeeCord.class.getDeclaredField("gson");
|
||||||
|
gsonField.setAccessible(true);
|
||||||
|
gsonField.set(bungeeCord, gson.newBuilder().registerTypeAdapter(ServerPing.class, this).create());
|
||||||
|
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
throw new SecurityException("Failed to inject ServerListPing", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(ServerPing ping, Type type, JsonSerializationContext context) {
|
||||||
|
JsonElement element = gson.toJsonTree(ping, type);
|
||||||
|
|
||||||
|
JsonObject object = element.getAsJsonObject();
|
||||||
|
object.addProperty("preventsChatReports", true);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerPing deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
return gson.fromJson(element, ServerPing.class);
|
||||||
|
}
|
||||||
|
}
|
59
src/de/steamwar/bungeecore/mods/WorldDownloader.java
Normale Datei
59
src/de/steamwar/bungeecore/mods/WorldDownloader.java
Normale Datei
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bungeecore.mods;
|
||||||
|
|
||||||
|
import de.steamwar.bungeecore.listeners.PluginMessage;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class WorldDownloader {
|
||||||
|
// https://wiki.vg/Plugin_channels/World_downloader
|
||||||
|
// https://github.com/Pokechu22/WorldDownloader-Serverside-Companion
|
||||||
|
// https://github.com/Pokechu22/WorldDownloader
|
||||||
|
|
||||||
|
private final byte[] controlPacket;
|
||||||
|
|
||||||
|
public WorldDownloader() {
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream out = new DataOutputStream(stream);
|
||||||
|
|
||||||
|
try {
|
||||||
|
out.writeInt(1); // basic data packet
|
||||||
|
out.writeBoolean(false); // General download enabled
|
||||||
|
out.writeInt(-1); // Save radius
|
||||||
|
out.writeBoolean(false); // Chunk caching enabled
|
||||||
|
out.writeBoolean(false); // Entity saving enabled
|
||||||
|
out.writeBoolean(false); // Tile entity saving disabled
|
||||||
|
out.writeBoolean(false); // Container saving disabled
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Could not create AntiWDL packet", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
controlPacket = stream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePluginMessage(PluginMessageEvent event) {
|
||||||
|
PluginMessage.send((ProxiedPlayer) event.getSender(), "WDL|CONTROL", "wdl:control", controlPacket);
|
||||||
|
}
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.network;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.BungeeCore;
|
|
||||||
import de.steamwar.bungeecore.commands.TeamCommand;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import de.steamwar.sql.SWException;
|
|
||||||
import de.steamwar.network.packets.NetworkPacket;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
|
||||||
import net.md_5.bungee.api.connection.Server;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
import net.md_5.bungee.event.EventPriority;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class NetworkReceiver extends BasicListener {
|
|
||||||
|
|
||||||
private static final List<String> blockedTags = Arrays.asList("bungeecord:main", "BungeeCord", "sw:bridge");
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
|
||||||
public void onPluginMessage(PluginMessageEvent event) {
|
|
||||||
if (blockedTags.contains(event.getTag()) && !TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) {
|
|
||||||
SWException.log(((InetSocketAddress) event.getSender().getSocketAddress()).getHostString() + " tried to send a plugin message with tag " + event.getTag(), Base64.getEncoder().encodeToString(event.getData()));
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!event.getTag().equals("sw:bridge"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
event.setCancelled(true);
|
|
||||||
if(!(event.getSender() instanceof Server))
|
|
||||||
return;
|
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bungeecore.network;
|
|
||||||
|
|
||||||
import de.steamwar.bungeecore.commands.TeamCommand;
|
|
||||||
import de.steamwar.bungeecore.listeners.BasicListener;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.connection.Server;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
import net.md_5.bungee.event.EventPriority;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
public class SWScriptSyntaxForwarder extends BasicListener {
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
|
||||||
public void onPluginMessage(PluginMessageEvent event) {
|
|
||||||
if (!event.getTag().equals("sw:script_syntax")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.setCancelled(true);
|
|
||||||
if (!(event.getSender() instanceof Server)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((ProxiedPlayer) event.getReceiver()).sendData("sw:script_syntax", event.getData());
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,9 +20,6 @@ DISABLED=§cCurrently disabled.
|
|||||||
|
|
||||||
SERVER_START_OVERLOAD=§cServer start cancelled due to overload. Please try again later.
|
SERVER_START_OVERLOAD=§cServer start cancelled due to overload. Please try again later.
|
||||||
|
|
||||||
#ModLoader blocker
|
|
||||||
MODLOADER_DENIED=§cYou cannot join arenas with LiteLoader or Fabric without the FabricModSender https://steamwar.de/downloads installed.
|
|
||||||
|
|
||||||
#Help command
|
#Help command
|
||||||
HELP_LOBBY=§7Return from anywhere to the lobby using §8/§el§7!
|
HELP_LOBBY=§7Return from anywhere to the lobby using §8/§el§7!
|
||||||
HELP_LOBBY_HOVER=§eBack to the lobby
|
HELP_LOBBY_HOVER=§eBack to the lobby
|
||||||
@ -118,10 +115,6 @@ MOD_RED_PLUR=Attempted use of mods:\n{0}
|
|||||||
MOD_YELLOW_SING=§7Deactivate the mod §e{0}§7 to continue playing on §eSteam§8War§7.
|
MOD_YELLOW_SING=§7Deactivate the mod §e{0}§7 to continue playing on §eSteam§8War§7.
|
||||||
MOD_YELLOW_PLUR=§7Deactivate the mods\n§e{0}\n§7to continue playing on §eSteam§8War§7.
|
MOD_YELLOW_PLUR=§7Deactivate the mods\n§e{0}\n§7to continue playing on §eSteam§8War§7.
|
||||||
|
|
||||||
MOD_LOADER_LUNAR_CLIENT=§eLunar Client§7 is not supported on §eSteam§8War§7.
|
|
||||||
|
|
||||||
MOD_USE_MODSENDER=§cPlease use the §c§lFabricModSender§c (https://steamwar.de/downloads/) or remove the mod that is rebranding your installation.
|
|
||||||
|
|
||||||
#Various commands
|
#Various commands
|
||||||
ALERT=§f{0}
|
ALERT=§f{0}
|
||||||
STAT_SERVER=§7Server §e{0}§8: §7Below limit §e{1} §7Server count §e{2}
|
STAT_SERVER=§7Server §e{0}§8: §7Below limit §e{1} §7Server count §e{2}
|
||||||
|
@ -16,9 +16,6 @@ DISABLED=§cDerzeit deaktiviert.
|
|||||||
|
|
||||||
SERVER_START_OVERLOAD=§cDer Serverstart wurde aufgrund von Überlastung abgebrochen. Versuche es später erneut.
|
SERVER_START_OVERLOAD=§cDer Serverstart wurde aufgrund von Überlastung abgebrochen. Versuche es später erneut.
|
||||||
|
|
||||||
#ModLoader blocker
|
|
||||||
MODLOADER_DENIED=§cMit LiteLoader oder Fabric kannst du keinen Arenen ohne den FabricModSender https://steamwar.de/downloads beitreten.
|
|
||||||
|
|
||||||
#Help command
|
#Help command
|
||||||
HELP_LOBBY=§7Kehre von überall mit §8/§el §7zur Lobby zurück!
|
HELP_LOBBY=§7Kehre von überall mit §8/§el §7zur Lobby zurück!
|
||||||
HELP_LOBBY_HOVER=§eZurück zur Lobby
|
HELP_LOBBY_HOVER=§eZurück zur Lobby
|
||||||
@ -103,10 +100,6 @@ MOD_RED_PLUR=Versuchte Benutzung der Mods:\n{0}
|
|||||||
MOD_YELLOW_SING=§7Deaktiviere den Mod §e{0}§7, um weiter auf §eSteam§8War §7spielen zu können.
|
MOD_YELLOW_SING=§7Deaktiviere den Mod §e{0}§7, um weiter auf §eSteam§8War §7spielen zu können.
|
||||||
MOD_YELLOW_PLUR=§7Deaktiviere die Mods\n§e{0}\n§7um weiter auf §eSteam§8War §7spielen zu können.
|
MOD_YELLOW_PLUR=§7Deaktiviere die Mods\n§e{0}\n§7um weiter auf §eSteam§8War §7spielen zu können.
|
||||||
|
|
||||||
MOD_LOADER_LUNAR_CLIENT=§eLunar Client§7 ist auf §eSteam§8War§7 nicht unterstützt.
|
|
||||||
|
|
||||||
MOD_USE_MODSENDER=§cBitte nutze den §c§lFabricModSender§c (https://steamwar.de/downloads/) oder entferne den Mod, der deine Fabric-Installation tarnt.
|
|
||||||
|
|
||||||
#Various commands
|
#Various commands
|
||||||
STAT_SERVER=§7Server §e{0}§8: §7Startfähig §e{1} §7Serveranzahl §e{2}
|
STAT_SERVER=§7Server §e{0}§8: §7Startfähig §e{1} §7Serveranzahl §e{2}
|
||||||
|
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren