Skip to content

Commit

Permalink
Correctly emulate vanilla behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
SoniEx2 committed Jan 7, 2016
1 parent a2b141c commit e18c596
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 24 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ plugins {
}
*/

version = "0.2.3"
version = "0.2.4"
group = "com.github.soniex2.notebetter" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "notebetter"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
guiFactory = "com.github.soniex2.notebetter.gui.NoteBetterGuiFactory")
public class NoteBetter {
public static final String MODID = "notebetter";
public static final String VERSION = "0.2.3";
public static final String VERSION = "0.2.4";

@Mod.Instance
public static NoteBetter instance;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/github/soniex2/notebetter/NoteMethods.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.NoteBlockEvent;

import java.util.logging.Logger;

/**
* @author soniex2
*/
Expand All @@ -37,6 +39,7 @@ private static boolean tryPlay(World world, BlockPos pos, NoteBetterInstrument i

public static boolean handleTileEntity(World world, BlockPos pos) {
if (world.getBlockState(pos.up()).getBlock().getMaterial() == Material.air) {
//NoteBetter.instance.log.info("PLAYNOTE: " + world.getWorldTime());
if (!(world.getTileEntity(pos) instanceof TileEntityNote)) return false;

TileEntityNote te = ((TileEntityNote) world.getTileEntity(pos));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import java.util.Map;

/**
* This class adds 2 things to 2 methods in 2 different classes. This class adds a simple hook to
* WorldServer.sendQueuedBlockEvents and a cancelable hook to TileEntityNote.triggerNote.
*
* @author soniex2
*/
public class NoteBetterClassTransformer implements IClassTransformer {
Expand All @@ -19,23 +22,35 @@ public class NoteBetterClassTransformer implements IClassTransformer {

private void setupDeobfMaps() {
maps = new HashMap<String, String>();
/*TileEntityNote*/
maps.put("te_mname", "triggerNote");
maps.put("te_mdesc", "(Lnet/minecraft/world/World;Lnet/minecraft/util/BlockPos;)V");
maps.put("nm_te_hndlr_desc", "(Lnet/minecraft/world/World;Lnet/minecraft/util/BlockPos;)Z");
/*WorldServer*/
maps.put("ws_mname", "sendQueuedBlockEvents");
maps.put("ws_mdesc", "()V");
maps.put("nm_ws_hook_desc", "(Lnet/minecraft/world/World;)V");
}

private void setupObfMaps() {
maps = new HashMap<String, String>();
/*TileEntityNote*/
maps.put("te_mname", "func_175108_a");
maps.put("te_mdesc", "(Lnet/minecraft/world/World;Lnet/minecraft/util/BlockPos;)V");
maps.put("nm_te_hndlr_desc", "(Lnet/minecraft/world/World;Lnet/minecraft/util/BlockPos;)Z");
/*WorldServer*/
maps.put("ws_mname", "func_147488_Z");
maps.put("ws_mdesc", "()V");
maps.put("nm_ws_hook_desc", "(Lnet/minecraft/world/World;)V");
}

@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
isObfuscated = !name.equals(transformedName);
if (transformedName.equals("net.minecraft.tileentity.TileEntityNote"))
return transformTileEntityNote(basicClass);
else if (transformedName.equals("net.minecraft.world.WorldServer"))
return transformWorldServer(basicClass);
return basicClass;
}

Expand All @@ -61,10 +76,10 @@ private byte[] transformTileEntityNote(byte[] basicClass) {
VarInsnNode vigetPos = new VarInsnNode(Opcodes.ALOAD, 2);
it.add(vigetPos);
MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
"com/github/soniex2/notebetter/NoteMethods",
"handleTileEntity",
maps.get("nm_te_hndlr_desc"),
false);
"com/github/soniex2/notebetter/NoteMethods",
"handleTileEntity",
maps.get("nm_te_hndlr_desc"),
false);
it.add(call);
LabelNode cont = new LabelNode();
JumpInsnNode test = new JumpInsnNode(Opcodes.IFEQ, cont);
Expand Down Expand Up @@ -94,4 +109,52 @@ private byte[] transformTileEntityNote(byte[] basicClass) {

return basicClass;
}

private byte[] transformWorldServer(byte[] basicClass) {
if (isObfuscated) setupObfMaps();
else setupDeobfMaps();

System.out.println("Transforming WorldServer");

try {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);

boolean transformed = false;

for (MethodNode m : classNode.methods) {
if (m.name.equals(maps.get("ws_mname")) && m.desc.equals(maps.get("ws_mdesc"))) {
ListIterator<AbstractInsnNode> it = m.instructions.iterator();
System.out.println("Transforming WorldServer.sendQueuedBlockEvents");
VarInsnNode vigetWorld = new VarInsnNode(Opcodes.ALOAD, 0);
it.add(vigetWorld);
MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
"com/github/soniex2/notebetter/event/WorkaroundHandler",
"sendNoteUpdates",
maps.get("nm_ws_hook_desc"),
false);
it.add(call);
System.out.println("Done!");
transformed = true;
}
}
if (!transformed)
System.out.println("Failed to transform WorldServer.sendQueuedBlockEvents");

ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);

if (transformed)
System.out.println("Transforming WorldServer - Success!");
else
System.out.println("Transforming WorldServer - Failed!");
return writer.toByteArray();
} catch (Exception e) {
System.out.println("Transforming WorldServer - Failed!");
e.printStackTrace();
}

return basicClass;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.soniex2.notebetter.event;

import com.github.soniex2.notebetter.NoteBetter;
import com.github.soniex2.notebetter.util.EventHelper;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumParticleTypes;
Expand All @@ -10,11 +11,9 @@
import net.minecraftforge.event.world.NoteBlockEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.*;

/**
* @author soniex2
Expand Down Expand Up @@ -48,6 +47,7 @@ public boolean equals(Object other) {
}

public void play(World world) {
NoteBetter.instance.log.info("PLAY: " + world.getWorldTime());
// Our event
/*NoteBetterEvent.Play event = new NoteBetterEvent.Play(world, pos, world.getBlockState(pos), note, instrument);
if (MinecraftForge.EVENT_BUS.post(event)) return;
Expand All @@ -74,31 +74,68 @@ public void play(World world) {
world.playSoundEffect(x, y, z, instrument.toString(), volume, pitch);
if (world instanceof WorldServer) // just in case it *isn't* being called from a WorldServer
((WorldServer) world).spawnParticle(EnumParticleTypes.NOTE, false, x, y + .7, z, 0, note / 24.0, 0.0, 0.0, 1.0);
//NoteBetter.instance.log.info("PLAYWORKAROUND: " + world.getWorldTime());
}
}

public static final Map<World, LinkedHashSet<NoteTickWorkaround>> map = Collections.synchronizedMap(new WeakHashMap<World, LinkedHashSet<NoteTickWorkaround>>());
public static final Map<World, NoteTickWorkaroundWorldData> map = Collections.synchronizedMap(new WeakHashMap<World, NoteTickWorkaroundWorldData>());

public static void addNoteTickWorkaround(World world, NoteTickWorkaround workaround) {
NoteTickWorkaroundWorldData data;
synchronized (map) {
LinkedHashSet<NoteTickWorkaround> set = map.get(world);
if (set == null) map.put(world, set = new LinkedHashSet<NoteTickWorkaround>());
set.add(workaround);
data = map.get(world);
if (data == null)
map.put(world, data = new NoteTickWorkaroundWorldData());
}
data.add(workaround);
}

@SubscribeEvent
/*@SubscribeEvent
public void onWorldTick(TickEvent.WorldTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.type == TickEvent.Type.WORLD) {
synchronized (map) {
for (Map.Entry<World, LinkedHashSet<NoteTickWorkaround>> entry : map.entrySet()) {
World w = entry.getKey();
LinkedHashSet<NoteTickWorkaround> set = entry.getValue();
for (NoteTickWorkaround ntw : set) {
ntw.play(w);
}
set.clear();
if (event.phase == TickEvent.Phase.END && event.type == TickEvent.Type.WORLD && event.side == Side.SERVER) {
NoteTickWorkaroundWorldData data = map.get(event.world);
if (data == null) return;
data.sendQueuedBlockEvents(event.world);
}
}*/

// This needs to happen between the start of the tick and TileEntity updates, but also after block ticks.
public static void sendNoteUpdates(World world) {
NoteTickWorkaroundWorldData data = map.get(world);
if (data == null) return;
data.sendQueuedBlockEvents(world);
}

@SubscribeEvent
public void onNotePlay(NoteBlockEvent.Play event) {
if (!event.world.isRemote) NoteBetter.instance.log.info("PLAYEVENT: " + event.world.getWorldTime());
}

private static class NoteTickWorkaroundWorldData {
private int blockEventCacheIndex = 0;
@SuppressWarnings("unchecked")
private List<NoteTickWorkaround>[] blockEventCaches = new ArrayList[] {
new ArrayList<NoteTickWorkaround>(), new ArrayList<NoteTickWorkaround>()
};

public synchronized void add(NoteTickWorkaround workaround) {
for (NoteTickWorkaround blockeventdata1 : blockEventCaches[blockEventCacheIndex]) {
if (blockeventdata1.equals(workaround)) return;
}

blockEventCaches[blockEventCacheIndex].add(workaround);
}

private synchronized void sendQueuedBlockEvents(World w) {
while (!blockEventCaches[blockEventCacheIndex].isEmpty()) {
int i = blockEventCacheIndex;
blockEventCacheIndex ^= 1;

for (NoteTickWorkaround blockeventdata : blockEventCaches[i]) {
blockeventdata.play(w);
}

this.blockEventCaches[i].clear();
}
}
}
Expand Down

0 comments on commit e18c596

Please sign in to comment.