Add a FML outbound target for all players tracking a point or entity in the world. Closes #3677 (#4631)

This commit is contained in:
Vincent Lee 2018-04-01 03:42:55 -05:00 committed by LexManos
parent f5c0b894bf
commit 1d7c04daeb
5 changed files with 273 additions and 0 deletions

View file

@ -150,3 +150,13 @@
{
this.func_187273_a(this.field_187282_b.func_72688_a().func_175625_s(blockpos1));
}
@@ -311,4 +348,9 @@
return d0;
}
+
+ public List<EntityPlayerMP> getWatchingPlayers()
+ {
+ return func_187274_e() ? java.util.Collections.unmodifiableList(field_187283_c) : java.util.Collections.emptyList();
+ }
}

View file

@ -25,6 +25,7 @@ import io.netty.channel.ChannelHandlerContext;
import java.util.EnumMap;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraftforge.fml.common.FMLCommonHandler;
@ -177,6 +178,33 @@ public class FMLEventChannel {
channels.get(Side.SERVER).writeAndFlush(pkt).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Send to all tracking the point
* The {@code range} field of the {@link NetworkRegistry.TargetPoint} is ignored.
* @param pkt
* @param point
*/
public void sendToAllTracking(FMLProxyPacket pkt, NetworkRegistry.TargetPoint point)
{
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TRACKING_POINT);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
channels.get(Side.SERVER).writeAndFlush(pkt).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Send to all tracking the entity
* This is not equivalent to {@link #sendToAllTracking(FMLProxyPacket, NetworkRegistry.TargetPoint)}
* because entities have different tracking distances based on their type.
* @param pkt
* @param entity
*/
public void sendToAllTracking(FMLProxyPacket pkt, Entity entity)
{
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TRACKING_ENTITY);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(entity);
channels.get(Side.SERVER).writeAndFlush(pkt).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Send to all in a dimension
* @param pkt

View file

@ -26,9 +26,17 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.AttributeKey;
import java.util.List;
import java.util.Set;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.management.PlayerChunkMap;
import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint;
@ -231,6 +239,79 @@ public class FMLOutboundHandler extends ChannelOutboundHandlerAdapter {
return builder.build();
}
},
/**
* The packet is sent to all players that are watching the Chunk containing the supplied {@link TargetPoint}.
* The {@code range} field of the {@link TargetPoint} is ignored.
*/
TRACKING_POINT(Sets.immutableEnumSet(Side.SERVER))
{
@Override
public void validateArgs(Object args)
{
if (!(args instanceof TargetPoint))
{
throw new RuntimeException("TRACKING_POINT expects a TargetPoint argument");
}
}
@Nullable
@Override
public List<NetworkDispatcher> selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet)
{
TargetPoint tp = (TargetPoint)args;
WorldServer world = DimensionManager.getWorld(tp.dimension);
if (world == null)
{
return ImmutableList.of();
}
PlayerChunkMapEntry entry = world.getPlayerChunkMap().getEntry(MathHelper.floor(tp.x) >> 4, MathHelper.floor(tp.z) >> 4);
if (entry == null)
{
return ImmutableList.of();
}
ImmutableList.Builder<NetworkDispatcher> builder = ImmutableList.builder();
for (EntityPlayerMP player : entry.getWatchingPlayers())
{
NetworkDispatcher dispatcher = player.connection.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
if (dispatcher != null) builder.add(dispatcher);
}
return builder.build();
}
},
/**
* The packet is sent to all players tracking the supplied {@link Entity}. This is different from {@link #TRACKING_POINT} because Entities
* can have different tracking distances depending on their type.
*/
TRACKING_ENTITY(Sets.immutableEnumSet(Side.SERVER))
{
@Override
public void validateArgs(Object args)
{
if (!(args instanceof Entity))
{
throw new RuntimeException("TRACKING_ENTITY expects an Entity argument");
}
}
@Nullable
@Override
public List<NetworkDispatcher> selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet)
{
Entity e = (Entity)args;
Set<? extends EntityPlayer> players = FMLCommonHandler.instance().getMinecraftServerInstance()
.getWorld(e.dimension).getEntityTracker().getTrackingPlayers(e);
ImmutableList.Builder<NetworkDispatcher> builder = ImmutableList.builder();
for (EntityPlayer player : players)
{
NetworkDispatcher dispatcher = ((EntityPlayerMP) player).connection.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
if (dispatcher != null) builder.add(dispatcher);
}
return builder.build();
}
},
/**
* The packet is sent to the server this client is currently conversing with.
* @author cpw

View file

@ -24,6 +24,7 @@ import io.netty.channel.ChannelFutureListener;
import java.lang.reflect.Method;
import java.util.EnumMap;
import net.minecraft.entity.Entity;
import net.minecraft.util.IThreadListener;
import io.netty.channel.ChannelHandler;
@ -263,6 +264,37 @@ public class SimpleNetworkWrapper {
channels.get(Side.SERVER).writeAndFlush(message).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Sends this message to everyone tracking a point.
* The {@link IMessageHandler} for this message type should be on the CLIENT side.
* The {@code range} field of the {@link TargetPoint} is ignored.
*
* @param message The message to send
* @param point The tracked {@link TargetPoint} around which to send
*/
public void sendToAllTracking(IMessage message, NetworkRegistry.TargetPoint point)
{
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TRACKING_POINT);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
channels.get(Side.SERVER).writeAndFlush(message).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Sends this message to everyone tracking an entity.
* The {@link IMessageHandler} for this message type should be on the CLIENT side.
* This is not equivalent to {@link #sendToAllTracking(IMessage, TargetPoint)}
* because entities have different tracking distances based on their type.
*
* @param message The message to send
* @param entity The tracked entity around which to send
*/
public void sendToAllTracking(IMessage message, Entity entity)
{
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TRACKING_ENTITY);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(entity);
channels.get(Side.SERVER).writeAndFlush(message).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
/**
* Send this message to everyone within the supplied dimension.
* The {@link IMessageHandler} for this message type should be on the CLIENT side.

View file

@ -0,0 +1,122 @@
package net.minecraftforge.debug;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.init.Items;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
@Mod(modid = TrackingTargetTest.ID, name = "Tracking Target Test", acceptableRemoteVersions = "*")
@Mod.EventBusSubscriber
public class TrackingTargetTest
{
public static final String ID = "trackingtargettest";
private static boolean ENABLED = false;
private static final SimpleNetworkWrapper NET = new SimpleNetworkWrapper(ID);
private static final Logger LOGGER = LogManager.getLogger(ID);
@Mod.EventHandler
public static void preinit(FMLPreInitializationEvent evt)
{
NET.registerMessage(TestMessageHandler.class, TestMessage.class, 0, Side.CLIENT);
NET.registerMessage(TestEntityMessageHandler.class, TestEntityMessage.class, 1, Side.CLIENT);
}
// Every 3 seconds, send a message to all players tracking overworld (500, 500).
// If you move sufficiently far away (i.e greater than the server render distance) from (500, 500), you should stop receiving the messages.
@SubscribeEvent
public static void tick(TickEvent.WorldTickEvent evt)
{
if (ENABLED && evt.side == Side.SERVER && evt.phase == TickEvent.Phase.END)
{
if (evt.world.getWorldTime() % 60 == 0)
{
NetworkRegistry.TargetPoint pt = new NetworkRegistry.TargetPoint(0, 500, 0, 500, -1);
NET.sendToAllTracking(new TestMessage(), pt);
}
}
}
public static class TestMessage implements IMessage
{
@Override
public void fromBytes(ByteBuf buf) {}
@Override
public void toBytes(ByteBuf buf) {}
}
public static class TestMessageHandler implements IMessageHandler<TestMessage, IMessage>
{
@Override
public IMessage onMessage(TestMessage message, MessageContext ctx)
{
LOGGER.info("Received tracking point test message");
return null;
}
}
// Every 3 seconds, send a message to all players tracking any item frame with a stick in it
// If you move sufficiently far away from the frame, you should stop receiving the messages.
private static final Set<EntityItemFrame> FRAMES = Collections.newSetFromMap(new WeakHashMap<>());
@SubscribeEvent
public static void frameJoin(EntityJoinWorldEvent evt)
{
if (ENABLED && !evt.getWorld().isRemote && evt.getEntity() instanceof EntityItemFrame)
{
FRAMES.add((EntityItemFrame) evt.getEntity());
}
}
@SubscribeEvent
public static void tickEntity(TickEvent.WorldTickEvent evt)
{
if (ENABLED && evt.side == Side.SERVER && evt.phase == TickEvent.Phase.END)
{
if (evt.world.getWorldTime() % 60 == 0)
{
for (EntityItemFrame frame : FRAMES)
{
if (!frame.isDead && !frame.getDisplayedItem().isEmpty() && frame.getDisplayedItem().getItem() == Items.STICK)
{
NET.sendToAllTracking(new TestEntityMessage(), frame);
}
}
}
}
}
public static class TestEntityMessage implements IMessage
{
@Override
public void fromBytes(ByteBuf buf) {}
@Override
public void toBytes(ByteBuf buf) {}
}
public static class TestEntityMessageHandler implements IMessageHandler<TestEntityMessage, IMessage>
{
@Override
public IMessage onMessage(TestEntityMessage message, MessageContext ctx)
{
LOGGER.info("Received tracking point test entity message");
return null;
}
}
}