Jump to content

[SOLVED] Trying to make IMessageHandler more generic


coolAlias

Recommended Posts

SOLUTION: Update Forge to 1121 or later; on 1060, the IMessage system is apparently still broken, as the following generic class worked perfectly as soon as I updated! Though some may view it as pointless, I think it's pretty f-ing sweet. Now if only I could change the names of fromBytes and toBytes to include 'read' and 'write'.... :P

 

Original post:

 

Hey all,

 

So, I decided to attempt to make an abstract class implementing IMessageHandler to do some of the monotonous work for me, like so:

 

public abstract class AbstractMessage<T extends IMessage> implements IMessage, IMessageHandler <T, IMessage> {

// auto-incrementing byte discriminators should really have been built in from the start... =.=
private static byte packetId = 0;

public static void registerMessages() {
// note that: OpenGuiMessage extends AbstractMessage<OpenGuiMessage>
TestMain.dispatcher.registerMessage(OpenGuiMessage.class, OpenGuiMessage.class, packetId++, Side.SERVER);
}

/**
* Handle a message received on the client side; messages can now only be handled on ONE side
* @return a message to send back to the Server, or null if no reply is necessary
*/
public abstract IMessage handleClientMessage(EntityPlayer player, T message, MessageContext ctx);

/**
* Handle a message received on the server side; messages can now only be handled on ONE side
* @return a message to send back to the Client, or null if no reply is necessary
*/
public abstract IMessage handleServerMessage(EntityPlayer player, T message, MessageContext ctx);

@Override
public IMessage onMessage(T message, MessageContext ctx) {
if (ctx.side == Side.CLIENT) {
return handleClientMessage(Minecraft.getMinecraft().thePlayer, message, ctx);
} else {
return handleServerMessage(ctx.getServerHandler().playerEntity, message, ctx);
}
}
}

 

 

While the code seems like it should work and compiles fine, netty/FML can't seem to figure out what to do with it:

 

java.lang.IllegalStateException: cannot determine the type of the type parameter 'REQ': class cpw.mods.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper
at io.netty.util.internal.TypeParameterMatcher.fail(TypeParameterMatcher.java:171) ~[TypeParameterMatcher.class:?]
at io.netty.util.internal.TypeParameterMatcher.find0(TypeParameterMatcher.java:165) ~[TypeParameterMatcher.class:?]
at io.netty.util.internal.TypeParameterMatcher.find(TypeParameterMatcher.java:93) ~[TypeParameterMatcher.class:?]
at io.netty.channel.SimpleChannelInboundHandler.<init>(SimpleChannelInboundHandler.java:60) ~[simpleChannelInboundHandler.class:?]
at io.netty.channel.SimpleChannelInboundHandler.<init>(SimpleChannelInboundHandler.java:50) ~[simpleChannelInboundHandler.class:?]
at cpw.mods.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper.<init>(SimpleChannelHandlerWrapper.java:17) ~[simpleChannelHandlerWrapper.class:?]
at cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper.getHandlerWrapper(SimpleNetworkWrapper.java:85) ~[simpleNetworkWrapper.class:?]
at cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper.addServerHandlerAfter(SimpleNetworkWrapper.java:73) ~[simpleNetworkWrapper.class:?]
at cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper.registerMessage(SimpleNetworkWrapper.java:63) ~[simpleNetworkWrapper.class:?]
at testingstuff.network.AbstractMessage.registerMessages(AbstractMessage.java:37) ~[AbstractMessage.class:?]

 

 

I know it's not really necessary, but I find it quite tedious (and messy-looking) having to write out "ctx.getServerHandler().playerEntity" every time I want to get the player, as well as recall which side I'm supposed to be on so that I get the correct player (yeah, it's not that hard, but I prefer it to be REALLY obvious). Making the abstract class do that work for me would just be sweet xD

 

Does anyone have any idea how (or if it's even possible) to get this working?

 

Much obliged,

coolAlias

Link to comment
Share on other sites

player = ctx.getHandler().playerEntity

From then on you only have to type 'player' BOOM magic...

Netty is all about generic data it needs a 1:1 ratio, it also needs to have non-abstract classes.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

player = ctx.getHandler().playerEntity

From then on you only have to type 'player' BOOM magic...

Netty is all about generic data it needs a 1:1 ratio, it also needs to have non-abstract classes.

I guess I need to update my Forge version then (1060 still... yeah, I know >.<); all I have is ctx.getClientHandler(), which doesn't even have a player field, and ctx.getServerHandler(); no generic version exists unless you count ctx.netHandler, but that also doesn't have a player.

 

Well, that will be one annoyance out of the way, sort of; I'd still prefer to just have the player as a parameter, but maybe that's just me. :P

 

Thanks for the reply, though.

Link to comment
Share on other sites

Its just not how netty works, you get a context that holds all the information and go from there.

There may not be a generic way of getting it my point is the 'everytime' argument is invalid as it's a basic programming setup to cache the values you need in easy to reference names.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

Its just not how netty works, you get a context that holds all the information and go from there.

There may not be a generic way of getting it my point is the 'everytime' argument is invalid as it's a basic programming setup to cache the values you need in easy to reference names.

Yeah, I understand - I just think it would be even more magical to have the EntityPlayer passed directly to the IMessageHandler#onMessage method (which is also a basic programming practice - passing variables that will likely be needed), but if for whatever reason that's not possible, it's not exactly difficult to create a local reference to a field. It's just irksome :P

 

At any rate, I was just curious if there was a way restructure it for myself using generics, both because it's an interesting exercise in Java and the result would appease my urge to economize the amount of typing I must do and the number of lines in my code. Most certainly not necessary, but an adventure in personal preference, if you will. Certainly you've restructured things to suit your personal tastes, before, no?

Link to comment
Share on other sites

Hi coolAlias

 

(welcome back, long time no see :) )

 

Just a thought - based on another thread on this forum recently, this method might cause you grief on the dedicated server because Minecraft doesn't exist there.

@Override
public IMessage onMessage(T message, MessageContext ctx) {
if (ctx.side == Side.CLIENT) {
return handleClientMessage(Minecraft.getMinecraft().thePlayer, message, ctx);
} else {
return handleServerMessage(ctx.getServerHandler().playerEntity, message, ctx);
}
}
}

Not having done much with 1.7.2 so far, I haven't yet figured out whether the best way to get around that is to put the side-specific stuff into one of the proxy classes, or to register two different handlers on the different sides (is that possible with IMessageHandler?)  Any thoughts?

 

-TGG

 

Link to comment
Share on other sites

Hi TGG,

 

Thanks for the welcome xD I was pretty busy moving and starting a new job, so didn't have much time for modding.

 

I'm pretty sure that the IMessageHandler#onMessage method is the final method called in the chain, so it will be either on the client or the server depending on which direction the packet was sent.

 

Granted, I have only just started messing around with this whole message system (since apparently the first Netty tutorial has a memory leak, though no one has bothered to explain where and why), so I could be wrong, but I assume that when MessageContext.side == CLIENT, that I am actually on the client side so Minecraft.getMinecraft() shouldn't be a problem; otherwise, how could anyone do any client-side processing of their packets? :\

 

Re: registering of message handlers, I don't think you need to worry about partitioning those into the proxy classes; the registration process looks to me like it should work by registering during the FML initialization events without worrying about Side at all, except for as an argument to pass to the method.

 

As far as I understand, IMessageHandler should be implemented by each packet (message), not as a generic catch-all handler like some people used in 1.6 and earlier.

 

All that being said, I've only played with this message stuff for about 2 hours so far, so take all of the above with a grain of salt ;)

 

Cheers,

coolAlias

Link to comment
Share on other sites

Thanks for the welcome xD I was pretty busy moving and starting a new job, so didn't have much time for modding.

Life gets in the way, huh :)  how inconvenient... :)

 

Granted, I have only just started messing around with this whole message system (since apparently the first Netty tutorial has a memory leak, though no one has bothered to explain where and why), so I could be wrong, but I assume that when MessageContext.side == CLIENT, that I am actually on the client side so Minecraft.getMinecraft() shouldn't be a problem; otherwise, how could anyone do any client-side processing of their packets? :\

 

Re: registering of message handlers, I don't think you need to worry about partitioning those into the proxy classes; the registration process looks to me like it should work by registering during the FML initialization events without worrying about Side at all, except for as an argument to pass to the method.

coolAlias

I've into the problem before that a Server-Side class which refers to Client Side classes cause a runtime error before a single line of code gets executed.  I don't know much about Java runtime linking but it seems that the first use of the Server-side class makes the jvm look for all the other classes referred to by the Server-Side class.  Those classes don't exist so it gives a runtime error.  Doesn't make any difference that the client-side classes are unreachable when the Server-side methods are called.

 

Could be I've misinterpreted the cause of the errors I was getting, but in the end I fixed it by moving all references to client-side classes into the proxy class.  It was quite painful actually. 

 

-TGG

 

 

 

 

 

Link to comment
Share on other sites

I've into the problem before that a Server-Side class which refers to Client Side classes cause a runtime error before a single line of code gets executed.  I don't know much about Java runtime linking but it seems that the first use of the Server-side class makes the jvm look for all the other classes referred to by the Server-Side class.  Those classes don't exist so it gives a runtime error.  Doesn't make any difference that the client-side classes are unreachable when the Server-side methods are called.

 

Could be I've misinterpreted the cause of the errors I was getting, but in the end I fixed it by moving all references to client-side classes into the proxy class.  It was quite painful actually. 

 

-TGG

Sure, that can happen if it's a Server-Side class, but this isn't :P If you take another look, you'll see that the Minecraft.getMinecraft() is embedded inside of a method that only gets called when a message (packet) is being processed; this only ever happens on one side or the other (at a time - it could happen on either depending on how you register it), which is why I check the Side first. It's effectively the same as saying "if (world.isRemote) getClientStuff; else getServerStuff", which I'm sure you've seen happen in other methods, e.g. Item#onRightClick, in which you could either spawn an entity (server) or particles (client), neither of which will crash if executed on the correct side.

 

I've definitely experienced what you are talking about though - it was with things like Render, Gui, KeyBindings and those kinds of classes. Basically, any time you are going to reference something that only exists on the client, you must segregate it somehow or you will run into the issue you described.

 

The main problem is for an entire CLASS, such as a Render class that you stuck @SideOnly(Side.CLIENT) above because it's everywhere else in the code (I do that...), or a client-only field that is initialized in-line (i.e. public IIcon[] icons = new IIcon[3] <- crash), when you go to register that class, there is no segregation of sides done by default, so you must either check yourself (using something like FML's getEffectiveSide if you don't have a world object) or delegate to the Proxy classes (much cleaner and easier).

 

Same idea for something like Minecraft.getMinecraft() - as long as you can segregate the call and ensure it happens on the client side, nothing averse will come of it.

 

Anyway, my AbstractMessage class seems to be working just fine now, so I'm pretty happy with that xD I still need to test it in a multiplayer scenario, but I suspect it won't have any problems.

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I'm using Modrinth as a launcher for a forge modpack on 1.20.1, and can't diagnose the issue on the crash log myself. Have tried repairing the Minecraft instillation as well as removing a few mods that have been problematic for me in the past to no avail. Crash log is below, if any further information is necessary let me know. Thank you! https://paste.ee/p/k6xnS
    • Hey folks. I am working on a custom "Mecha" entity (extended from LivingEntity) that the player builds up from blocks that should get modular stats depending on the used blocks. e.g. depending on what will be used for the legs, the entity will have a different jump strength. However, something unexpected is happening when trying to override a few of LivingEntity's functions and using my new own "Mecha" specific fields: instead of their actual instance-specific value, the default value is used (0f for a float, null for an object...) This is especially strange as when executing with the same entity from a point in the code specific to the mecha entity, the correct value is used. Here are some code snippets to better illustrate what I mean: /* The main Mecha class, cut down for brevity */ public class Mecha extends LivingEntity { protected float jumpMultiplier; //somewhere later during the code when spawning the entity, jumpMultiplier is set to something like 1.5f //changing the access to public didn't help @Override //Overridden from LivingEntity, this function is only used in the jumpFromGround() function, used in the aiStep() function, used in the LivingEntity tick() function protected float getJumpPower() { //something is wrong with this function //for some reason I can't correctly access the fields and methods from the instanciated entity when I am in one of those overridden protected functions. this is very annoying LogUtils.getLogger().info(String.valueOf(this.jumpMultiplier))) //will print 0f return this.jumpMultiplier * super.getJumpPower(); } //The code above does not operate properly. Written as is, the entity will not jump, and adding debug logs shows that when executing the code, the value of this.jumpMultiplier is 0f //in contrast, it will be the correct value when done here: @Override public void tick() { super.tick(); //inherited LivingEntity logic //Custom logic LogUtils.getLogger().info(String.valueOf(this.jumpMultiplier))) //will print 1.5f } } My actual code is slightly different, as the jumpMuliplier is stored in another object (so I am calling "this.legModule.getJumpPower()" instead of the float), but even using a simple float exactly like in the code above didn't help. When running my usual code, the object I try to use is found to be null instead, leading to a crash from a nullPointerException. Here is the stacktrace of said crash: The full code can be viewed here. I have found a workaround in the case of jump strength, but have already found the same problem for another parameter I want to do, and I do not understand why the code is behaving as such, and I would very much like to be able to override those methods as intended - they seemed to work just fine like that for vanilla mobs... Any clues as to what may be happening here?
    • Please delete post. Had not noticed the newest edition for 1.20.6 which resolves the issue.
    • https://paste.ee/p/GTgAV Here's my debug log, I'm on 1.18.2 with forge 40.2.4 and I just want to get it to work!! I cant find any mod names in the error part and I would like some help from the pros!! I have 203 mods at the moment.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.