Add a FML outbound target for all players tracking a point or entity in the world. Closes #3677 (#4631)
This commit is contained in:
parent
f5c0b894bf
commit
1d7c04daeb
5 changed files with 273 additions and 0 deletions
|
@ -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();
|
||||
+ }
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
122
src/test/java/net/minecraftforge/debug/TrackingTargetTest.java
Normal file
122
src/test/java/net/minecraftforge/debug/TrackingTargetTest.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue