package com.matt.forgehax.mods.services; import static com.matt.forgehax.Helper.getLog; import com.google.common.util.concurrent.FutureCallback; import com.matt.forgehax.asm.events.PacketEvent; import com.matt.forgehax.events.PlayerConnectEvent; import com.matt.forgehax.util.SimpleTimer; import com.matt.forgehax.util.command.Setting; import com.matt.forgehax.util.entity.PlayerInfo; import com.matt.forgehax.util.entity.PlayerInfoHelper; import com.matt.forgehax.util.mod.ServiceMod; import com.matt.forgehax.util.mod.loader.RegisterMod; import com.mojang.authlib.GameProfile; import java.util.Objects; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; import joptsimple.internal.Strings; import net.minecraft.network.play.server.SPacketChunkData; import net.minecraft.network.play.server.SPacketCustomPayload; import net.minecraft.network.play.server.SPacketPlayerListItem; import net.minecraft.network.play.server.SPacketPlayerListItem.Action; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.FMLNetworkEvent; /** * Created on 7/18/2017 by fr1kin */ @RegisterMod public class ScoreboardListenerService extends ServiceMod { private final Setting<Integer> wait = getCommandStub() .builders() .<Integer>newSettingBuilder() .name("wait") .description("Time to wait after joining world") .defaultTo(5000) .build(); private final Setting<Integer> retries = getCommandStub() .builders() .<Integer>newSettingBuilder() .name("retries") .description("Number of times to attempt retries on failure") .defaultTo(1) .build(); private final SimpleTimer timer = new SimpleTimer(); private boolean ignore = false; public ScoreboardListenerService() { super("ScoreboardListenerService", "Listens for player joining and leaving"); } private void fireEvents( SPacketPlayerListItem.Action action, PlayerInfo info, GameProfile profile) { if (ignore || info == null) { return; } switch (action) { case ADD_PLAYER: { MinecraftForge.EVENT_BUS.post(new PlayerConnectEvent.Join(info, profile)); break; } case REMOVE_PLAYER: { MinecraftForge.EVENT_BUS.post(new PlayerConnectEvent.Leave(info, profile)); break; } } } @SubscribeEvent public void onClientConnect(FMLNetworkEvent.ClientConnectedToServerEvent event) { ignore = false; } @SubscribeEvent public void onClientDisconnect(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) { ignore = false; } @SubscribeEvent public void onPacketIn(PacketEvent.Incoming.Pre event) { if (ignore && timer.isStarted() && timer.hasTimeElapsed(wait.get())) { ignore = false; } if (!ignore && event.getPacket() instanceof SPacketCustomPayload) { ignore = true; timer.start(); } else if (ignore && event.getPacket() instanceof SPacketChunkData) { ignore = false; timer.reset(); } } @SubscribeEvent public void onScoreboardEvent(PacketEvent.Incoming.Pre event) { if (event.getPacket() instanceof SPacketPlayerListItem) { final SPacketPlayerListItem packet = event.getPacket(); if (!Action.ADD_PLAYER.equals(packet.getAction()) && !Action.REMOVE_PLAYER.equals(packet.getAction())) { return; } packet .getEntries() .stream() .filter(Objects::nonNull) .filter( data -> !Strings.isNullOrEmpty(data.getProfile().getName()) || data.getProfile().getId() != null) .forEach( data -> { final String name = data.getProfile().getName(); final UUID id = data.getProfile().getId(); final AtomicInteger retries = new AtomicInteger(this.retries.get()); PlayerInfoHelper.registerWithCallback( id, name, new FutureCallback<PlayerInfo>() { @Override public void onSuccess(@Nullable PlayerInfo result) { fireEvents(packet.getAction(), result, data.getProfile()); } @Override public void onFailure(Throwable t) { if (retries.getAndDecrement() > 0) { getLog() .warn( "Failed to lookup " + name + "/" + id.toString() + ", retrying (" + retries.get() + ")..."); PlayerInfoHelper.registerWithCallback( data.getProfile().getId(), name, this); } else { t.printStackTrace(); PlayerInfoHelper.generateOfflineWithCallback(name, this); } } }); }); } } }