Posted April 8, 2016 (edited) I've been trying to modify a spell to make it's effects more flexible (using a property file). However I've been getting exceptions when I run Mod Loader "compile error: no such class: $4" .. this is repeated for the 4 parameters $1 through $4. I've been following the footsteps of another mod, and I'm stumped as to why mine isn't working.. see the full source below: Spoiler public class ConfigureDirtMod implements WurmMod, Configurable, PreInitable { public int amount = 6; public int castingTime = 10; public int cost = 20; public int difficulty = 50; public int level = 40; public long cooldown = ; @Override public void configure(Properties properties) { amount = Integer.parseInt(properties.getProperty("giveAmount", Integer.toString(amount))); castingTime = Integer.parseInt(properties.getProperty("castingTime", Integer.toString(castingTime))); cost = Integer.parseInt(properties.getProperty("cost", Integer.toString(cost))); difficulty = Integer.parseInt(properties.getProperty("difficulty", Integer.toString(difficulty))); level = Integer.parseInt(properties.getProperty("level", Integer.toString(level))); cooldown = Long.parseLong(properties.getProperty("cooldown", Long.toString(cooldown))); } @Override public void preInit() { try { CtConstructor construct = HookManager.getInstance().getClassPool() .getCtClass("com.wurmonline.server.spells.Dirt").getDeclaredConstructor(new CtClass[]{}); construct.instrument(new Construct()); CtClass skill = HookManager.getInstance().getClassPool().getCtClass("com.wurmonline.server.skills.Skill"); CtClass creature = HookManager.getInstance().getClassPool().getCtClass("com.wurmonline.server.creatures.Creature"); CtClass item = HookManager.getInstance().getClassPool().getCtClass("com.wurmonline.server.items.Item"); CtMethod doEffect = HookManager.getInstance().getClassPool() .getCtClass("com.wurmonline.server.spells.Dirt").getDeclaredMethod("doEffect", new CtClass[]{skill,CtClass.doubleType,creature,item}); doEffect.instrument(new DoEffect()); } catch (NotFoundException | CannotCompileException e) { throw new HookException(e); } } class Construct extends ExprEditor { public void edit(MethodCall m) { //String aName, int aNum, int aCastingTime, int aCost, int aDifficulty, int aLevel, long cooldown try { m.replace( "super(\"Dirt\", 453, "+castingTime+", "+cost+", "+difficulty+", "+level+", "+cooldown+");"+ "targetTile = true;"+ "targetItem = true;"+ "description = \"creates and destroys dirt\";" ); } catch (CannotCompileException e) { e.printStackTrace(); } } } class DoEffect extends ExprEditor { public void edit(MethodCall m) { try { //Skill castSkill, double power, Creature performer, Item target) m.replace("int sizeLeft = $4.getFreeVolume();"+ "try {"+ "ItemTemplate template = ItemTemplateFactory.getInstance().getTemplate(26);"+ "boolean created = false;"+ "int nums = Math.min("+amount+", sizeLeft / template.getVolume());"+ "if ($4.isCrate()) {"+ "nums = Math.min("+amount+", $4.getRemainingCrateSpace());"+ "}"+ "if (nums > 0) {"+ "if ($4.isBulkContainer()) {"+ "Item dirt = ItemFactory.createItem(26, (float)$2, $3.getName());"+ "dirt.setWeight(template.getWeightGrams() * nums, true);"+ "dirt.setMaterial(template.getMaterial());"+ "if ($4.isCrate()) {"+ "dirt.AddBulkItemToCrate($3, $4);"+ "} else {"+ "dirt.AddBulkItem($3, $4);"+ "}"+ "created = true; }"+ "else {"+ "for (int x = 0; x <= nums; x++) {"+ "if ($4.getOwnerId() == $3.getWurmId()) {"+ "if (!$3.canCarry(template.getWeightGrams())) {"+ "if (created) {"+ "$3.getCommunicator().sendNormalServerMessage(\"You create some dirt.\", (byte)2);"+ "}"+ "}"+ "}"+ "else if (!$4.mayCreatureInsertItem())"+ "{"+ "if (created)"+ "$3.getCommunicator().sendNormalServerMessage(\"You create some dirt.\", (byte)2);"+ "$_ = null;"+ "}"+ "Item dirt = ItemFactory.createItem(26, (float)$2, $3.getName());"+ "$4.insertItem(dirt);"+ "created = true;"+ "}"+ "}"+ "}"+ "if (created) {"+ "$3.getCommunicator().sendNormalServerMessage(\"You create some dirt.\", (byte)2);"+ "} }"+ "catch (NoSuchTemplateException localNoSuchTemplateException) {}catch (FailedException localFailedException) {}"); } catch (CannotCompileException e) { e.printStackTrace(); } } } } Edited April 10, 2016 by Merivo Problem Solved Share this post Link to post Share on other sites
Posted April 9, 2016 I'm not exactly sure what's wrong but I think I see a problem. http://jboss-javassist.github.io/javassist/tutorial/tutorial2.html#add read the part about "Substituting source text for an existing expression" which is relavent to what your doing. 1. I don't think try catch is supported within the "m.replace(~~stuff~~)". 2. You may need to surround your code with {} as it's a block...so m.replace("{ ~~ stuff ~~ }"); I usually take a different path and try to make methods in my mod using full on Java code so my IDE works with the code. Then, I use setbody to copy the method from the mod and overwrite the method in WU. I did this in loadingUnchainedMod regarding setfire... /** * This method is a reference method used by ctmSetFire to copy and replace the same method in WU. * @param performer is type Creature and is the entity doing the action. * @param target is type Item and is the item where fire is to be set. * @return success indicator. The modded equivalency of this is that it always succeeds. */ Share this post Link to post Share on other sites
Posted April 9, 2016 doEffect.instrument(new DoEffect()); This will not replace the method "doEffect". It will replace any method call in doEffect with your code. This is probabaly failing if there are method calls with less than 4 arguments. To replace the complete method with different code you'll need doEffect.setBody(code) or copy an existing method with setBody(ctOtherMethod, classMap) as joedobo said. Share this post Link to post Share on other sites
Posted April 10, 2016 (edited) Thanks for the replies! Sorry about the delay, I was out all day yesterday. If try/catch isn't supported, is throws{}? As for your different approach joedobo, I would certainly rather do it the way you suggest but the only thing I can't figure out is how to give doEffect the value of my variable amount; since it's going in a different class how's that going to work? Back to the original approach, I've wrapped {} around the method, and am now using setbody. That fixed the old exceptions, now I'm getting a new exception: "CannotCompileException: no such class: ItemTemplate". I didn't come up with that part of the code myself, this was from the original Dirt.class, shouldn't it already have imported ItemTemplate? Or is it that my ConfigureDirtMod needs to import ItemTemplate? Oh and, quick question. Which is called first, configure or preInit? Edited April 10, 2016 by Merivo Grammar Share this post Link to post Share on other sites
Posted April 10, 2016 So, I've been having a look at your LoadingUnchainedMod source, and this code throws an exception when I try this out in my own class, but not in yours: ClassPool pool = HookManager.getInstance().getClassPool(); CtClass ctcSelf = pool.makeClass("Default"); try { ctcSelf = pool.get(this.getClass().getName()); } catch (NotFoundException e) { e.printStackTrace(); } javassist.NotFoundException: org.gotti.wurmunlimited.mods.configuredirt.ConfigureDirtMod.. Any ideas? Share this post Link to post Share on other sites
Posted April 10, 2016 The modloader calls configure() for all mods, then preInit(), then init(). The import statements are only used in the compiler. The resulting bytecode will use full class names and all info about imported packages is lost. So you'll either have to write the full qualified name in setBody() or tell the class pool that you want to use imports: pool.importPackage("com.wurmonline.server.spells") The class pool is only set up to cover the main class loader. By default all modules are loaded in their own classloader unless the sharedClassLoader property is set in the module configuration. This helps to isolate modules so they can't interfere with eachother. But this also means that any classes from the module are unavailable from the core server code. Share this post Link to post Share on other sites
Posted April 10, 2016 Thank you very much ago, that information was very helpful! The mod's now working Share this post Link to post Share on other sites
Posted April 11, 2016 Just read this. Ago is helpful. He also helped me a lot when I first started. It's nice when someone knows exactly why something doesn't work. Regarding the way I referred too, I prefer to have java code that my ide can inspect. Java code as quoted strings is a hassle. Merivo, it seems you got things working so this may not be that useful. but, I make methods in my mods that basically could be directly copied and pasted into WU with the one stipulation that the ClassMap will replace all occurences of my mod's class name with the WU class I'm putting the mod in. In order to do this I need to use the method.setbody(CtMethod method, ClassMap map). I don't know of another way to get a CtMethod object other than through CtClass. Getting a CtClass and CtMethod object for the mod requires using the shared class loader. Share this post Link to post Share on other sites