Sign in to follow this  
Ricowan

I can't figure out how to make this mod work

Recommended Posts

Hello modding gurus.  I'm trying to make a seemingly simple mod but I can't get it to work.

 

I'm trying to modify the sendWho method in the LoginHandler class.  For now, I just want to change one line of code (but more later).

Line 22 of the method looks like this:

if (player.getPower() > 0) {

 

I want to change it to this:

if (player.getPower() >= 0) {

 

I don't think ExprEditor from Javassist is the way to do this, at least not as far as I can tell.

 

I've tried to hook the entire method to replace it but, since one of the method parameters is a player object, I'm having tons of issues:

 

try {
	HookManager.getInstance().registerHook("com.wurmonline.server.LoginHandler", "sendWho", "()V", new InvocationHandlerFactory() {
	@Override
	public InvocationHandler createInvocationHandler() {
		return new InvocationHandler() {
		@Override
		public Object invoke(Object object, Method method, Object[] args) throws Throwable {
			if (args[0].isUndead()) {
				return;
			}

Maven complains that args[0].isUndead() is an unknown entity.  I'm stumped!  Any help?

 

Share this post


Link to post
Share on other sites

this might work in an ExprEditor on the getPower() method:

methodCall.replace("$_ == 0 ? 1 : $_");

 

 

Another option is to used bytecode:

LoginHandler.class sendWho()
ConstPool cp = classPool.get("com.wurmonline.server.LoginHandler").getClassFile().getConstPool(); 

Bytecode search = new Bytecode(cp);
...construct a unique search sequence to match what's in WU. You'll replace this with "replace".

Bytecode replace = new Bytecode(cp);
...Construct a replace sequence. CodeReplacer will automatically makes space so the replace string will fit and at the very 
least completely replaces "search".
 

CodeReplacer codeReplacer = new CodeReplacer(sendWho.getCodeAttribute()); // this isn't valid. I'm being lazy and don't want
to look up the steps to get the CodeAttribute for sendWho().
codeReplacer.replaceCode(search.get(), replace.get());

sendWho.getMethodInfo().rebuildStackMapIf6(classPool, LoginHandler.getClassFile()); // this may not be needed. If you get a 
bunch of weird bytecode errors during server startup add that. And again some of those "get" methods aren't valid. You'll 
need to figure out the proper steps to get the objects.

 

Share this post


Link to post
Share on other sites

Thanks Joe!  But to be honest, I am clueless about the bytecode stuff.  I looked at the bytecode for the line of code in question (I think) but it just doesn't make any sense to me, let alone know what to change.

 

For the ExprEditor, the main problem is that player.GetPower() is called several times in this method, and I only want to modify the first instance.  Changing player.GetPower() to return a 0 instead of the actual player power (which I believe is what your replace is doing?) would work in the first instance but it would mess up the logic later in the method.

 

I really think hooking the method and modifying it is the way to go, but for some reason it's not working.

 

Share this post


Link to post
Share on other sites

I'm commenting on how I'd likely do this. I'm sure there are many different ways to do it.

 

sendWho_CtMethod.instrument(new ExprEditor() {
	@Override
	public void edit(MethodCall methodCall) throws CannotCompileException {
		if ("getPower".equals(methodCall.getMethodName()) && methodCall.getLineNumber() == 123) {
			logger.log(Level.FINE, "sendWho method,  edit call to " +
					methodCall.getMethodName() + " at index " + methodCall.getLineNumber());
			methodCall.replace("$_ = $_ == 0 ? 1 : $_;");
		}
	}
});

^^^ the number "123" is fake. You'll need to look at the bytecode to find out the actual number. Using this you can isolate a specific call to a method that shows up multiple times. I think I made a mistake in the previous post for "replace" code and its altered here.

 

I don't blame you for avoiding bytecode.

 

I'm not familiar with Ago's HookManager tool.

Edited by joedobo

Share this post


Link to post
Share on other sites

Thanks again Joe, but I think I have the hook version figured out. :)  I had to instantiate new object and assign the args to the new objects instead of using the args objects directly.  A few other kludges to handle non-public methods and everything compiles correctly.  Now to test it!

 

Share this post


Link to post
Share on other sites

And, of course, it doesn't work.  It compiles without error, but putting it in the mods folder and launching the server gives me this error:

SEVERE org.gotti.wurmunlimited.serverlauncher.DelegatedLauncher: javassist.NotFoundException: sendWho(..) is not found in com.wurmonline.server.LoginHandler

 

WTF?  The method is there, I've checked with two different de-compiles.  I have the signature of the method correct...  Is the problem that sendWho is a static method?  What do I do in this case?  

 

Share this post


Link to post
Share on other sites

Have you been able to figure out a solution to this? I would love for every user to be able to use the /who function.

Share this post


Link to post
Share on other sites

maybe take a look at this mod to see how they did it

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this