Skip to content

Instantly share code, notes, and snippets.

@effective-light
Last active June 20, 2017 07:12
Show Gist options
  • Select an option

  • Save effective-light/55050e4b0deae596443d to your computer and use it in GitHub Desktop.

Select an option

Save effective-light/55050e4b0deae596443d to your computer and use it in GitHub Desktop.
Byte-code engineering
/*
* Copyright (c) 2014 hamzaxx
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package packagename;
import com.google.common.base.Preconditions;
import javassist.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class NmsEntity
{
private static Class<?> nmsWorld;
private static Constructor<?> constructor;
private static Constructor<?> pathfinderConstructor;
private static Method worldHandle;
private static Method setLocation;
private static Method addEntity;
private static Method getBukkitEntity;
private static String version;
private static Field goalSelector;
private static Field methodProfiler;
private static Field init;
private Plugin plugin;
static
{
version = Bukkit.getServer().getClass().getPackage().getName().split( "\\." )[ 3 ];
try
{
Class<?> craftWorld = Class.forName( "org.bukkit.craftbukkit." + version + ".CraftWorld" );
Class<?> nmsEntity = Class.forName( "net.minecraft.server." + version + ".Entity" );
Class<?> entityInsentient = Class.forName( "net.minecraft.server." + version + ".EntityInsentient" );
Class<?> pathfinderGoalSelector = Class.forName( "net.minecraft.server." + version + ".PathfinderGoalSelector" );
Class<?> methodProfilerClass = Class.forName( "net.minecraft.server." + version + ".MethodProfiler" );
nmsWorld = Class.forName( "net.minecraft.server." + version + ".World" );
methodProfiler = nmsWorld.getField( "methodProfiler" );
pathfinderConstructor = pathfinderGoalSelector.getConstructor( methodProfilerClass );
worldHandle = craftWorld.getMethod( "getHandle" );
addEntity = nmsWorld.getMethod( "addEntity", nmsEntity );
goalSelector = entityInsentient.getField( "goalSelector" );
} catch ( ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e )
{
e.printStackTrace();
}
}
protected NmsEntity(Plugin plugin, String name, int id, String... methods)
{
this.plugin = plugin;
try
{
Class<?> entity = createClass( name, new NMSUtils().getName( id ), version, methods );
Preconditions.checkNotNull( entity, "entity can't be null!" );
constructor = entity.getConstructor( nmsWorld );
getBukkitEntity = entity.getMethod( "getBukkitEntity" );
setLocation = entity.getMethod( "setLocation", double.class, double.class, double.class, float.class, float.class );
init = entity.getField( "init" );
new NMSUtils().registerEntity( name, id, entity );
} catch ( NoSuchMethodException | NoSuchFieldException e )
{
e.printStackTrace();
}
}
protected Class<?> createClass(String name, String className, String version, String... methods)
{
String classPath = "temp." + name;
ClassPool pool = ClassPool.getDefault();
try
{
return Class.forName( classPath );
} catch ( ClassNotFoundException e )
{
pool.importPackage( "net.minecraft.server." + version );
CtClass clazz = pool.makeClass( classPath );
try
{
clazz.setSuperclass( pool.get( className ) );
clazz.addConstructor( CtNewConstructor.make( "public " + name + "(World world) { super(world); }", clazz ) );
clazz.addField( CtField.make( "public boolean init = false;", clazz ) );
for ( String method : methods )
{
clazz.addMethod( CtMethod.make( method, clazz ) );
}
clazz.writeFile();
return clazz.toClass();
} catch ( CannotCompileException | NotFoundException | IOException ex )
{
ex.printStackTrace();
}
}
return null;
}
public Entity spawn(Location location)
{
try
{
Object mcWorld = worldHandle.invoke( location.getWorld() );
Object npc = constructor.newInstance( mcWorld );
goalSelector.set( npc, pathfinderConstructor.newInstance( methodProfiler.get( mcWorld ) ) );
setLocation.invoke( npc, location.getX(), location.getY(),
location.getZ(), location.getYaw(), location.getPitch() );
addEntity.invoke( mcWorld, npc );
new BukkitRunnable()
{
@Override
public void run()
{
try
{
init.set( npc, true );
} catch ( IllegalAccessException e )
{
e.printStackTrace();
}
}
}.runTaskLater( plugin, 40L );
return ( Entity ) getBukkitEntity.invoke( npc );
} catch ( IllegalAccessException | InvocationTargetException | InstantiationException e )
{
e.printStackTrace();
}
return null;
}
}
/*
* Copyright (c) 2014 hamzaxx
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package packagename;
import org.bukkit.Bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class NMSUtils
{
private static Class<?> entityTypes;
private static Class<?> biomeBase;
private static Class<?> biomeMeta;
private static Method getFromInt;
static
{
String version = Bukkit.getServer().getClass().getPackage().getName().split( "\\." )[ 3 ];
try
{
entityTypes = Class.forName( "net.minecraft.server." + version + ".EntityTypes" );
biomeBase = Class.forName( "net.minecraft.server." + version + ".BiomeBase" );
biomeMeta = Class.forName( "net.minecraft.server." + version + ".BiomeBase$BiomeMeta" );
getFromInt = entityTypes.getDeclaredMethod( "a", int.class );
} catch ( ClassNotFoundException | NoSuchMethodException e )
{
e.printStackTrace();
}
}
public String getName(int id)
{
try
{
Class<?> clazz = ( Class<?> ) getFromInt.invoke( null, id );
if ( clazz != null )
{
return clazz.getName();
}
} catch ( IllegalAccessException | InvocationTargetException e )
{
e.printStackTrace();
}
return null;
}
public void registerEntity(String name, int id, Class<?> customClass)
{
try
{
Class<?> nmsClass = ( Class<?> ) getFromInt.invoke( null, id );
List<Map<?, ?>> dataMaps = new ArrayList<>();
for ( Field f : entityTypes.getDeclaredFields() )
{
if ( f.getType().getSimpleName().equals( Map.class.getSimpleName() ) )
{
f.setAccessible( true );
dataMaps.add( ( Map<?, ?> ) f.get( null ) );
}
}
if ( dataMaps.get( 2 ).containsKey( id ) )
{
dataMaps.get( 0 ).remove( name );
dataMaps.get( 2 ).remove( id );
}
Method method = entityTypes.getDeclaredMethod( "a", Class.class, String.class, int.class );
method.setAccessible( true );
method.invoke( null, customClass, name, id );
for ( Field f : biomeBase.getDeclaredFields() )
{
if ( f.getType().getSimpleName().equals( biomeBase.getSimpleName() ) )
{
if ( f.get( null ) != null )
{
for ( Field list : biomeBase.getDeclaredFields() )
{
if ( list.getType().getSimpleName().equals( List.class.getSimpleName() ) )
{
list.setAccessible( true );
@SuppressWarnings("unchecked")
List<Object> metaList = ( List<Object> ) list.get( f.get( null ) );
for ( Object meta : metaList )
{
Field clazz = biomeMeta.getDeclaredFields()[ 0 ];
if ( clazz.get( meta ).equals( nmsClass ) )
{
clazz.set( meta, customClass );
}
}
}
}
}
}
}
} catch ( InvocationTargetException | IllegalAccessException
| NoSuchMethodException e )
{
e.printStackTrace();
}
}
}
/*
* Copyright (c) 2014 hamzaxx
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package packagename;
import org.bukkit.Location;
import org.bukkit.entity.Villager;
import org.bukkit.plugin.Plugin;
public class NmsVillager extends NmsEntity
{
public NmsVillager(Plugin plugin)
{
super( plugin, "NmsVillager", 120,
"public void move(double d0, double d1, double d2) { if ( !init ) { super.move( d0, d1, d2 ); } }",
"public boolean a(EntityHuman entityhuman) { return false; }",
"public void a_(ItemStack itemstack) {}",
"public void a(MerchantRecipe merchantrecipe) {}",
"public double e(double d0, double d1, double d2) { return 0.0D; }",
"public void e(float f, float f1) {}",
"protected String bp() { return \"\"; }",
"protected String bo() { return \"\"; }",
"protected String z() { return \"\"; }" );
}
public Villager spawn(Location location)
{
return ( Villager ) super.spawn( location );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment