/* * This file is part of NeptuneVanilla, licensed under the MIT License (MIT). * * Copyright (c) 2015-2017, Jamie Mansfield <https://github.com/jamierocks> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.neptunepowered.vanilla.mixin.minecraft.server.management; import co.aikar.timings.NeptuneTimings; import com.mojang.authlib.GameProfile; import net.canarymod.Canary; import net.canarymod.ToolBox; import net.canarymod.Translator; import net.canarymod.api.ConfigurationManager; import net.canarymod.api.PlayerListAction; import net.canarymod.api.PlayerListData; import net.canarymod.api.entity.living.humanoid.Player; import net.canarymod.api.packet.Packet; import net.canarymod.api.world.DimensionType; import net.canarymod.api.world.World; import net.canarymod.bansystem.Ban; import net.canarymod.chat.MessageReceiver; import net.canarymod.config.Configuration; import net.canarymod.config.ServerConfiguration; import net.canarymod.hook.player.ConnectionHook; import net.canarymod.hook.player.PlayerListHook; import net.canarymod.hook.player.PreConnectionHook; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.S38PacketPlayerListItem; import net.minecraft.server.MinecraftServer; import net.minecraft.server.management.ServerConfigurationManager; import net.minecraft.stats.StatisticsFile; import net.minecraft.util.IChatComponent; import net.minecraft.world.WorldSettings; import org.apache.logging.log4j.Logger; import org.neptunepowered.vanilla.util.helper.StatisticsHelper; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.text.SimpleDateFormat; import java.util.List; import java.util.Map; import java.util.UUID; @Mixin(ServerConfigurationManager.class) @Implements(@Interface(iface = ConfigurationManager.class, prefix = "config$")) public abstract class MixinServerConfigurationManager implements ConfigurationManager { @Shadow @Final public static Logger logger; @Shadow @Final private static SimpleDateFormat dateFormat; @Shadow @Final private MinecraftServer mcServer; @Shadow @Final public List<EntityPlayerMP> playerEntityList; @Shadow @Final private Map<UUID, StatisticsFile> playerStatFiles; @Shadow protected int maxPlayers; @Shadow public abstract int getCurrentPlayerCount(); @Shadow public abstract List<EntityPlayerMP> getPlayerList(); @Shadow public abstract EntityPlayerMP getPlayerByUsername(String username); @Shadow public abstract int getMaxPlayers(); /** * @author jamierocks - 8th May 2016 * @reason Complete overwrite to use Canary internals. */ @Overwrite public String allowUserToConnect(SocketAddress address, GameProfile profile) { final ServerConfiguration srv = Configuration.getServerConfig(); final String ip = ((InetSocketAddress) address).getAddress().getHostAddress(); final PreConnectionHook hook = (PreConnectionHook) new PreConnectionHook(ip, profile.getName(), profile.getId(), DimensionType.NORMAL, Canary.getServer().getDefaultWorldName()).call(); if (hook.getKickReason() != null) { return hook.getKickReason(); } if (Canary.bans().isBanned(profile.getId().toString())) { final Ban ban = Canary.bans().getBan(profile.getId().toString()); if (ban.getExpiration() != -1) { return ban.getReason() + ", " + srv.getBanExpireDateMessage() + ToolBox.formatTimestamp(ban.getExpiration()); } return ban.getReason(); } if (Canary.bans().isIpBanned(ip)) { return Translator.translate(srv.getDefaultBannedMessage()); } if (!Canary.whitelist().isWhitelisted(profile.getId().toString()) && Configuration.getServerConfig().isWhitelistEnabled()) { return srv.getWhitelistMessage(); } if (this.playerEntityList.size() >= this.maxPlayers) { if (Canary.reservelist().isSlotReserved(profile.getId().toString()) && Configuration.getServerConfig().isReservelistEnabled()) { return null; } return srv.getServerFullMessage(); } return null; } /** * @author jamierocks - 28th October 2016 * @reason Use the global stats directory */ @Overwrite public StatisticsFile getPlayerStatsFile(EntityPlayer playerIn) { final UUID uuid = playerIn.getUniqueID(); StatisticsFile statisticsFile = this.playerStatFiles.get(uuid); if (statisticsFile == null) { statisticsFile = StatisticsHelper.getStatisticsFile(playerIn.getUniqueID(), playerIn.getName()); this.playerStatFiles.put(uuid, statisticsFile); } return statisticsFile; } @Redirect(method = "playerLoggedOut", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/management/ServerConfigurationManager;" + "sendPacketToAllPlayers(Lnet/minecraft/network/Packet;)V")) public void firePlayerListData(ServerConfigurationManager manager, net.minecraft.network.Packet packetIn, EntityPlayerMP playerIn) { PlayerListData playerListData = ((Player) playerIn).getPlayerListData(PlayerListAction.REMOVE_PLAYER); for (EntityPlayerMP playerMP : manager.playerEntityList) { PlayerListHook playerListHook = new PlayerListHook(playerListData.copy(), (Player) playerMP); if (!playerListHook.call().isCanceled()) { S38PacketPlayerListItem packet = new S38PacketPlayerListItem(); packet.action = S38PacketPlayerListItem.Action.valueOf(PlayerListAction.REMOVE_PLAYER.name()); WorldSettings.GameType gameType = WorldSettings.GameType.getByID(playerListHook.getData().getMode().getId()); IChatComponent iChatComponent = playerListHook.getData().displayNameSet() ? (IChatComponent) playerListHook.getData().getDisplayName() : null; packet.players.add(packet.new AddPlayerData(playerListHook.getData() .getProfile(), playerListHook.getData().getPing(), gameType, iChatComponent)); playerMP.playerNetServerHandler.sendPacket(packet); } } } @Inject(method = "initializeConnectionToPlayer", at = @At("RETURN")) public void sendMOTD(NetworkManager netManager, EntityPlayerMP playerIn, CallbackInfo info) { Canary.motd().sendMOTD((MessageReceiver) playerIn); } @Inject(method = "saveAllPlayerData", at = @At("HEAD")) public void onSaveAllPlayerData(CallbackInfo ci) { NeptuneTimings.savePlayersTimer.startTiming(); } @Inject(method = "saveAllPlayerData", at = @At("RETURN")) public void afterSaveAllPlayerData(CallbackInfo ci) { NeptuneTimings.savePlayersTimer.stopTiming(); } @Redirect(method = "initializeConnectionToPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/management/ServerConfigurationManager;" + "sendChatMsg(Lnet/minecraft/util/IChatComponent;)V")) public void fireConnectionHook(ServerConfigurationManager manager, IChatComponent component, NetworkManager netManager, EntityPlayerMP playerIn) { ConnectionHook hook = (ConnectionHook) new ConnectionHook( (Player) playerIn, component.getUnformattedTextForChat(), false // TODO: check first time ).call(); if (!hook.isHidden()) { manager.sendChatMsg(component); } } @Override public void sendPacketToAllInWorld(String world, Packet packet) { } @Override public int getNumPlayersOnline() { return this.getCurrentPlayerCount(); } @Override public Player getPlayerByName(String name) { return (Player) this.getPlayerByUsername(name); } @Override public List<Player> getAllPlayers() { return (List) this.getPlayerList(); } @Intrinsic public int config$getMaxPlayers() { return this.getMaxPlayers(); } @Override public void markBlockNeedsUpdate(int x, int y, int z, DimensionType dimension, String world) { } @Override public void switchDimension(Player player, World world, boolean createPortal) { } }