feat: ASM based packet abstraction

This commit is contained in:
LordMZTE 2024-05-04 17:07:10 +02:00
parent fcab7822fb
commit a2f1d5f4c1
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
10 changed files with 415 additions and 22 deletions

View file

@ -76,6 +76,10 @@ processResources {
task deobfJar(type: Jar) {
from sourceSets.main.output
classifier = 'deobf'
manifest {
attributes "FMLAT": "anvillib_at.cfg"
}
}
task sourcesJar(type: Jar) {

View file

@ -10,9 +10,7 @@ import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStoppedEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.relauncher.Side;
import net.anvilcraft.anvillib.network.AnvilChannel;
import net.anvilcraft.anvillib.network.PacketUpdateUserCache;
import net.anvilcraft.anvillib.proxy.CommonProxy;
import net.anvilcraft.anvillib.usercache.UserCache;
@ -30,7 +28,7 @@ public class AnvilLib {
)
public static CommonProxy proxy;
public static SimpleNetworkWrapper channel;
public static AnvilChannel channel;
@EventHandler
public static void preInit(FMLPreInitializationEvent ev) {
@ -43,14 +41,8 @@ public class AnvilLib {
new Thread(() -> proxy.saveUserCache(UserCache.INSTANCE))
);
channel = NetworkRegistry.INSTANCE.newSimpleChannel("anvillib");
int pktid = 0;
channel.registerMessage(
PacketUpdateUserCache.Handler.class,
PacketUpdateUserCache.class,
pktid++,
Side.CLIENT
);
channel = new AnvilChannel("anvillib");
channel.register(PacketUpdateUserCache.class);
}
@EventHandler

View file

@ -0,0 +1,16 @@
package net.anvilcraft.anvillib.asm;
/**
* A simple wrapper around the current ClassLoader to allow defining classes manually.
*/
public class ASMClassLoader extends ClassLoader {
public static final ASMClassLoader INSTANCE = new ASMClassLoader();
private ASMClassLoader() {
super(ASMClassLoader.class.getClassLoader());
}
public Class<?> define(String name, byte[] code) {
return super.defineClass(name, code, 0, code.length);
}
}

View file

@ -0,0 +1,91 @@
package net.anvilcraft.anvillib.network;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.anvilcraft.alec.jalec.factories.AlecCriticalRuntimeErrorExceptionFactory;
import net.anvilcraft.anvillib.asm.ASMClassLoader;
public class AnvilChannel extends SimpleNetworkWrapper {
private int nextID;
public AnvilChannel(String channelName) {
super(channelName);
}
@SuppressWarnings({ "unchecked", "ALEC" })
public void register(Class<? extends IAnvilPacket> clazz) {
AnvilPacket anno = clazz.getAnnotation(AnvilPacket.class);
if (anno == null)
throw AlecCriticalRuntimeErrorExceptionFactory.PLAIN.createAlecException(
"Packet class", clazz, "is missing AnvilPacket annotation!"
);
String name
= "net.anvilcraft.asm." + clazz.getName().replace('.', '_') + "#EventHandler";
ClassWriter cw = new ClassWriter(0);
cw.visit(
V1_6,
ACC_PUBLIC | ACC_SUPER,
name.replace('.', '/'),
null,
"java/lang/Object",
new String[] { Type.getInternalName(IMessageHandler.class) }
);
cw.visitSource("alechoefler.java", null);
{
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC,
"onMessage",
"(Lcpw/mods/fml/common/network/simpleimpl/IMessage;Lcpw/mods/fml/common/network/simpleimpl/MessageContext;)Lcpw/mods/fml/common/network/simpleimpl/IMessage;",
null,
null
);
mv.visitCode();
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(
INVOKEINTERFACE,
"net/anvilcraft/anvillib/network/IAnvilPacket",
"handle",
"(Lcpw/mods/fml/common/network/simpleimpl/MessageContext;)V",
true
);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 3);
mv.visitEnd();
}
cw.visitEnd();
Class<?> handlerClass = ASMClassLoader.INSTANCE.define(name, cw.toByteArray());
try {
this.registerMessage(
(IMessageHandler<IAnvilPacket, IMessage>) handlerClass.newInstance(),
clazz,
this.nextID++,
anno.value()
);
} catch (Exception e) {
throw AlecCriticalRuntimeErrorExceptionFactory.PLAIN
.createAlecExceptionWithCause(e, "Failed to instantiate ASM'd class");
}
}
}

View file

@ -0,0 +1,20 @@
package net.anvilcraft.anvillib.network;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cpw.mods.fml.relauncher.Side;
/**
* This annotation must be present on every AnvilLib packet to declare metadata about it.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnvilPacket {
/**
* The side the packet is sent to.
*/
public Side value();
}

View file

@ -0,0 +1,9 @@
package net.anvilcraft.anvillib.network;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import cpw.mods.fml.relauncher.Side;
public interface IAnvilPacket extends IMessage {
public void handle(MessageContext ctx);
}

View file

@ -5,13 +5,14 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import cpw.mods.fml.relauncher.Side;
import io.netty.buffer.ByteBuf;
import net.anvilcraft.alec.jalec.AlecLogger;
import net.anvilcraft.anvillib.usercache.UserCache;
public class PacketUpdateUserCache implements IMessage {
@AnvilPacket(Side.CLIENT)
public class PacketUpdateUserCache implements IAnvilPacket {
public Map<UUID, String> entries;
public PacketUpdateUserCache(Map<UUID, String> entries) {
@ -47,12 +48,9 @@ public class PacketUpdateUserCache implements IMessage {
}
}
public static class Handler
implements IMessageHandler<PacketUpdateUserCache, IMessage> {
@Override
public IMessage onMessage(PacketUpdateUserCache pkt, MessageContext arg1) {
UserCache.INSTANCE.users.putAll(pkt.entries);
return null;
}
@Override
public void handle(MessageContext ctx) {
UserCache.INSTANCE.users.putAll(this.entries);
AlecLogger.PLAIN.alec("AAAAALEEEEEC");
}
}

View file

@ -0,0 +1,10 @@
package net.anvilcraft.anvillib.util;
public class AnvilUtil {
public static <T extends Enum<T>> T enumFromInt(Class<T> clazz, int n) {
T[] values = clazz.getEnumConstants();
if (n < 0 || n >= values.length)
return null;
return values[n];
}
}

View file

@ -0,0 +1,155 @@
package net.anvilcraft.anvillib.vector;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
/**
* A 3-Dimensional vector represented as 3 doubles.
*/
public class Vec3 {
public double x;
public double y;
public double z;
public Vec3(Vec3 vec) {
this.x = vec.x;
this.y = vec.y;
this.z = vec.z;
}
public Vec3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vec3(Entity ent) {
this(ent.posX, ent.posY, ent.posZ);
}
public Vec3(TileEntity te) {
this(te.xCoord, te.yCoord, te.zCoord);
}
public Vec3(MovingObjectPosition mop) {
this(mop.blockX, mop.blockY, mop.blockZ);
}
/**
* Reads this Vec3 from a byte buf.
*
* @see writeToByteBuf
*/
public static Vec3 readFromByteBuf(ByteBuf buf) {
return new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble());
}
public int intX() {
return (int) Math.floor(this.x);
}
public int intY() {
return (int) Math.floor(this.y);
}
public int intZ() {
return (int) Math.floor(this.z);
}
/**
* Convert this Vec3 to a WorldVec, adding a given world.
*/
public WorldVec withWorld(World world) {
return new WorldVec(world, this.x, this.y, this.z);
}
/**
* Writes this Vec3 to a byte buffer.
*
* @see readFromByteBuf
*/
public void writeToByteBuf(ByteBuf buf) {
buf.writeDouble(this.x);
buf.writeDouble(this.y);
buf.writeDouble(this.z);
}
/**
* Offsets this BlockVec in the given direction by the given amount.
*
* @param dir The direction to move in.
* @param amt The amount to move by.
*
* @return this. This BlockVec is also modified.
*/
public Vec3 offset(ForgeDirection dir, double amt) {
this.x += dir.offsetX * amt;
this.y += dir.offsetY * amt;
this.z += dir.offsetZ * amt;
return this;
}
/**
* Offsets this BlockVec by one block in the given direction.
*
* @param dir The direction to move in.
*
* @return this. This BlockVec is also modified.
*/
public Vec3 offset(ForgeDirection dir) {
return this.offset(dir, 1);
}
/**
* Moves the Vec3 by the given offset. Modifies this.
*/
public Vec3 offset(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
/**
* Moves the Vec3 by the given vector. Modifies this.
*/
public Vec3 offset(Vec3 other) {
return this.offset(other.x, other.y, other.z);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(x);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(z);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vec3 other = (Vec3) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
return false;
return true;
}
}

View file

@ -0,0 +1,98 @@
package net.anvilcraft.anvillib.vector;
import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
/**
* An extension of a Vec3 that also includes a World.
*/
public class WorldVec extends Vec3 {
public World world;
public WorldVec(WorldVec wv) {
super(wv);
this.world = wv.world;
}
public WorldVec(World world, double x, double y, double z) {
super(x, y, z);
this.world = world;
}
public WorldVec(World world, Vec3 vec) {
super(vec);
this.world = world;
}
public WorldVec(Entity ent) {
super(ent);
this.world = ent.worldObj;
}
public WorldVec(TileEntity te) {
super(te);
this.world = te.getWorldObj();
}
/**
* Gets the block at the position pointed to by this vector.
*/
public Block getBlock() {
return this.world.getBlock(this.intX(), this.intY(), this.intZ());
}
public int getBlockMeta() {
return this.world.getBlockMetadata(this.intX(), this.intY(), this.intZ());
}
/**
* Gets the tile entity at the position pointed to by this vector.
*/
public TileEntity getTileEntity() {
return this.world.getTileEntity(this.intX(), this.intY(), this.intZ());
}
public void markForUpdate() {
this.world.markBlockForUpdate(this.intX(), this.intY(), this.intZ());
}
public void markForRenderUpdate() {
this.world.markBlockRangeForRenderUpdate(
this.intX(), this.intY(), this.intZ(), this.intX(), this.intY(), this.intZ()
);
}
public TargetPoint targetPoint(double range) {
return new TargetPoint(
this.world.provider.dimensionId, this.x, this.y, this.z, range
);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((world == null) ? 0 : world.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
WorldVec other = (WorldVec) obj;
if (world == null) {
if (other.world != null)
return false;
} else if (world != other.world)
return false;
return true;
}
}