diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/status/ServerStatusNetHandlerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/status/ServerStatusNetHandlerMixin.java new file mode 100644 index 00000000..add6ab84 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/status/ServerStatusNetHandlerMixin.java @@ -0,0 +1,133 @@ +package io.izzel.arclight.common.mixin.core.network.status; + +import com.mojang.authlib.GameProfile; +import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.IPacket; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.ServerStatusResponse; +import net.minecraft.network.status.ServerStatusNetHandler; +import net.minecraft.network.status.server.SServerInfoPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.SharedConstants; +import net.minecraft.util.text.StringTextComponent; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v.CraftServer; +import org.bukkit.craftbukkit.v.util.CraftIconCache; +import org.bukkit.entity.Player; +import org.bukkit.util.CachedServerIcon; +import org.jetbrains.annotations.NotNull; +import org.spigotmc.SpigotConfig; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +@Mixin(ServerStatusNetHandler.class) +public class ServerStatusNetHandlerMixin { + + @Shadow @Final private MinecraftServer server; + + @Redirect(method = "processServerQuery", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;sendPacket(Lnet/minecraft/network/IPacket;)V")) + private void arclight$handleServerPing(NetworkManager networkManager, IPacket packetIn) { + Object[] players = this.server.getPlayerList().players.toArray(); + class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent { + + CraftIconCache icon; + + ServerListPingEvent() { + super(((InetSocketAddress) networkManager.getRemoteAddress()).getAddress(), server.getMOTD(), server.getPlayerList().getMaxPlayers()); + this.icon = ((CraftServer) Bukkit.getServer()).getServerIcon(); + } + + @Override + public void setServerIcon(CachedServerIcon icon) { + if (!(icon instanceof CraftIconCache)) { + throw new IllegalArgumentException(icon + " was not created by " + CraftServer.class); + } + this.icon = (CraftIconCache) icon; + } + + @Override + @NotNull + public Iterator iterator() throws UnsupportedOperationException { + class Itr implements Iterator { + + int i; + int ret = Integer.MIN_VALUE; + ServerPlayerEntity player; + + @Override + public boolean hasNext() { + if (this.player != null) { + return true; + } + Object[] currentPlayers = players; + for (int length = currentPlayers.length, i = this.i; i < length; ++i) { + ServerPlayerEntity player = (ServerPlayerEntity) currentPlayers[i]; + if (player != null) { + this.i = i + 1; + this.player = player; + return true; + } + } + return false; + } + + @Override + public Player next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } + ServerPlayerEntity player = this.player; + this.player = null; + this.ret = this.i - 1; + return ((ServerPlayerEntityBridge) player).bridge$getBukkitEntity(); + } + + @Override + public void remove() { + Object[] currentPlayers = players; + int i = this.ret; + if (i < 0 || currentPlayers[i] == null) { + throw new IllegalStateException(); + } + currentPlayers[i] = null; + } + } + return new Itr(); + } + } + ServerListPingEvent event = new ServerListPingEvent(); + Bukkit.getPluginManager().callEvent(event); + List profiles = new ArrayList<>(players.length); + Object[] array; + for (int length = (array = players).length, i = 0; i < length; ++i) { + Object player = array[i]; + if (player != null) { + profiles.add(((ServerPlayerEntity) player).getGameProfile()); + } + } + ServerStatusResponse.Players playerSample = new ServerStatusResponse.Players(event.getMaxPlayers(), profiles.size()); + if (!profiles.isEmpty()) { + Collections.shuffle(profiles); + profiles = profiles.subList(0, Math.min(profiles.size(), SpigotConfig.playerSample)); + } + playerSample.setPlayers(profiles.toArray(new GameProfile[0])); + ServerStatusResponse ping = new ServerStatusResponse(); + ping.setFavicon(event.icon.value); + ping.setServerDescription(new StringTextComponent(event.getMotd())); + ping.setPlayers(playerSample); + int version = SharedConstants.getVersion().getProtocolVersion(); + ping.setVersion(new ServerStatusResponse.Version(this.server.getServerModName() + " " + this.server.getMinecraftVersion(), version)); + networkManager.sendPacket(new SServerInfoPacket(ping)); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java index 4978d814..4af3bc7b 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java @@ -2,6 +2,7 @@ package io.izzel.arclight.common.mixin.core.world.server; import com.google.common.collect.Lists; import io.izzel.arclight.common.bridge.entity.EntityBridge; +import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge; import io.izzel.arclight.common.bridge.inventory.IInventoryBridge; import io.izzel.arclight.common.bridge.world.ExplosionBridge; import io.izzel.arclight.common.bridge.world.server.ServerWorldBridge; @@ -16,6 +17,7 @@ import net.minecraft.entity.effect.LightningBoltEntity; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.IInventory; import net.minecraft.network.IPacket; +import net.minecraft.network.play.server.SSpawnParticlePacket; import net.minecraft.particles.IParticleData; import net.minecraft.profiler.IProfiler; import net.minecraft.server.MinecraftServer; @@ -44,6 +46,7 @@ import org.bukkit.event.server.MapInitializeEvent; import org.bukkit.event.weather.LightningStrikeEvent; import org.bukkit.event.world.WorldSaveEvent; import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Implements; import org.spongepowered.asm.mixin.Interface; import org.spongepowered.asm.mixin.Mixin; @@ -58,6 +61,7 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; import java.util.Random; import java.util.concurrent.Executor; import java.util.logging.Level; @@ -73,6 +77,7 @@ public abstract class ServerWorldMixin extends WorldMixin implements ServerWorld @Shadow public abstract int spawnParticle(T type, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed); @Shadow protected abstract boolean sendPacketWithinDistance(ServerPlayerEntity player, boolean longDistance, double posX, double posY, double posZ, IPacket packet); @Shadow @Nonnull public abstract MinecraftServer shadow$getServer(); + @Shadow @Final private List players; // @formatter:on public void arclight$constructor(MinecraftServer serverIn, Executor executor, SaveHandler saveHandler, WorldInfo worldInfo, DimensionType dimType, IProfiler profiler, IChunkStatusListener listener) { @@ -86,6 +91,19 @@ public abstract class ServerWorldMixin extends WorldMixin implements ServerWorld bridge$getWorld(); } + public int sendParticles(final ServerPlayerEntity sender, final T t0, final double d0, final double d1, final double d2, final int i, final double d3, final double d4, final double d5, final double d6, final boolean force) { + SSpawnParticlePacket packet = new SSpawnParticlePacket((T) t0, force, d0, d1, d2, (float) d3, (float) d4, (float) d5, (float) d6, i); + int j = 0; + for (ServerPlayerEntity entity : this.players) { + if (sender == null || ((ServerPlayerEntityBridge) entity).bridge$getBukkitEntity().canSee(((ServerPlayerEntityBridge) sender).bridge$getBukkitEntity())) { + if (this.sendPacketWithinDistance(entity, force, d0, d1, d2, packet)) { + ++j; + } + } + } + return j; + } + @Inject(method = "onEntityAdded", at = @At("RETURN")) private void arclight$validEntity(Entity entityIn, CallbackInfo ci) { ((EntityBridge) entityIn).bridge$setValid(true); diff --git a/arclight-common/src/main/resources/mixins.arclight.core.json b/arclight-common/src/main/resources/mixins.arclight.core.json index 9c14a206..c5a7d20b 100644 --- a/arclight-common/src/main/resources/mixins.arclight.core.json +++ b/arclight-common/src/main/resources/mixins.arclight.core.json @@ -298,6 +298,7 @@ "network.play.server.SChatPacketMixin", "network.play.server.SWorldBorderPacketMixin", "network.rcon.RConConsoleSourceMixin", + "network.status.ServerStatusNetHandlerMixin", "potion.EffectMixin", "server.CustomServerBossInfoMixin", "server.MinecraftServerMixin",